Skip to content

nemanjan00/dev-environment

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

62 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dev-environment

Build

Screenshot

Docker-based dev environment for running Claude Code in a sandboxed container, with optional VM isolation via Vagrant/libvirt for safe Docker-in-Docker access.

Also works as a standalone portable IDE with Neovim, tmux, zsh, and language tooling.

Table of contents

Build it

# Build base image
docker build -t nemanjan00/dev:base .

# Build a profile (default, reversing, etc.)
docker build -t nemanjan00/dev:default profiles/default/
docker build -t nemanjan00/dev:reversing profiles/reversing/

# With custom UID/GID (to match your host user) — apply to the base image
docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) -t nemanjan00/dev:base .

Profiles

The image is split into a base layer and profile-specific layers. The base image (nemanjan00/dev:base) contains the common dev environment (zsh, Neovim, tmux, Node.js, Python, Claude Code). Profiles extend it with domain-specific tools.

Profile Tag Description
default nemanjan00/dev:default Base environment, no extras
reversing nemanjan00/dev:reversing Reverse engineering: radare2, r2ghidra, r2mcp, apktool, binwalk

To use a profile with the CLI scripts:

bin/claude-docker --profile reversing

bin/claude-vm --profile reversing

Creating a new profile

Add a directory under profiles/ with a Dockerfile that extends the base image:

FROM nemanjan00/dev:base

USER 0
RUN pacman -Syu --noconfirm your-packages-here
USER 1000

CI automatically discovers and builds all profiles under profiles/.

Run it

docker run -ti nemanjan00/dev:default

Opening project inside of it

docker run -ti -eTERM=xterm-256color -v$(pwd):/work/project nemanjan00/dev:default zsh -ic "cd project ; tmux"

Claude Code

Claude Code is pre-installed. The quickest way to get started is with the CLI scripts:

# Direct Docker — simple, no VM overhead
bin/claude-docker

# VM-isolated — Claude gets its own Docker daemon, fully sandboxed
bin/claude-vm

Both scripts auto-detect and mount ~/.claude.json (OAuth), ~/.claude/ (config), and ~/.gitconfig into the container. Claude runs with --dangerously-skip-permissions since the environment is sandboxed.

Authentication

# API key (works with both scripts)
ANTHROPIC_API_KEY=sk-... bin/claude-docker

# OAuth — just run `claude login` on the host first, then:
bin/claude-docker  # ~/.claude.json is mounted automatically

What gets mounted

Host path Container path Purpose
~/.claude.json /work/.claude.json OAuth credentials (from claude login)
~/.claude /work/.claude Full Claude config (settings, memory, CLAUDE.md)
~/.gitconfig /work/.gitconfig Git identity and settings (read-only)

The container ships with a default ~/.claude/CLAUDE.md that documents the environment for Claude. When you mount your own ~/.claude, you can add your own settings, custom skills, and memory files.

Host network mode

Frontend developers who need container ports (e.g. dev servers) accessible on the host can enable host networking:

bin/claude-docker --host-network

This passes --network host to Docker, so any ports the container listens on are directly available on localhost.

Manual Docker usage

# Minimal
docker run -ti -e ANTHROPIC_API_KEY -v$(pwd):/work/project nemanjan00/dev:default zsh -ic "cd project ; claude"

# Full setup
docker run -ti -e ANTHROPIC_API_KEY \
  -v$(pwd):/work/project \
  -v~/.claude:/work/.claude \
  -v~/.claude.json:/work/.claude.json \
  -v~/.gitconfig:/work/.gitconfig:ro \
  nemanjan00/dev:default zsh -ic "cd project ; claude"

VM isolation

For full isolation (e.g. giving Claude access to Docker), use bin/claude-vm to run the dev container inside a lightweight VM via Vagrant + libvirt. Each invocation creates an ephemeral VM that is destroyed on exit.

Prerequisites

# Arch Linux
pacman -S vagrant libvirt qemu-full qemu-img
vagrant plugin install vagrant-libvirt

Usage

# Run from your project directory — it gets mounted into the VM
cd /path/to/project
/path/to/dev-environment/bin/claude-vm

# With API key
ANTHROPIC_API_KEY=sk-... bin/claude-vm

By default, claude-vm auto-detects ~/.claude.json, ~/.claude/, and ~/.gitconfig. You can override with env vars:

Env var Default Purpose
PROJECT_DIR $(pwd) Project directory to mount
CLAUDE_CONFIG_DIR ~/.claude Claude config (settings, memory)
CLAUDE_AUTH ~/.claude.json OAuth credentials file
ANTHROPIC_API_KEY (none) API key authentication

VMs are ephemeral — each claude-vm invocation gets a unique VM ID and cleans up on exit (including Ctrl-C). Multiple instances can run in parallel.

The container inside the VM has access to the VM's Docker socket (with correct group permissions via --group-add), so Claude can spin up additional containers as needed, fully isolated from the host.

How it works

Host (your machine)
└── Vagrant/libvirt VM (Alpine Linux, 4GB RAM, 2 vCPUs)
    ├── Docker daemon
    └── dev container (this image)
        ├── Claude Code (--dangerously-skip-permissions)
        ├── Docker CLI → VM's Docker socket
        ├── Neovim, tmux, zsh
        └── Project files (virtiofs mount)

Project files are mounted into the VM via virtiofs (native libvirt filesystem passthrough), so changes are reflected in both directions. The dev container cannot affect the host.

Troubleshooting

If vagrant up fails with dnsmasq: failed to create listening socket ... Address already in use, you have something bound on port 53 that conflicts with libvirt's DHCP. Run sudo bin/claude-vm-setup to create a custom network with DNS disabled.

Components

Supported languages

  • CSS
  • Dockerfile
  • HTML (with emmet support)
  • JS (eslint and tsserver)
  • JSON
  • PHP
  • Python
  • Bash
  • SQL
  • VimL
  • XML
  • YAML
  • Much more (via coc.nvim extensions)

Author

About

My docker-based dev environment

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors