This reference outlines the Docker workflow for a Linux dev environment container on Windows. The workflow uses batch scripts to automate container management, emphasizes declarative updates via Dockerfile, versioned images, persistent containers for day-to-day use, and periodic rebuilds for dependency freshness or major changes. The workflow separates container starting from shell attachment to prevent accidental shutdowns when closing shells—all interactions use exec for shells, and the container runs a keep-alive process (e.g., tail -f /dev/null) to stay active independently.
- Images: Immutable, single image per configuration. Rebuild replaces the previous image.
- Containers: Persistent (no
--rm); usestop/startfor sessions. Create new ones only after image updates. Container runs detached with a keep-alive command to avoid shutdown on shell exits. - Persistence: Work files are stored in a subdirectory (configurable via
WORK_FOLDERin.env.container) that is mounted into the container, keeping Docker setup files separate from work. - Security: Secrets (API keys, tokens) are loaded from
secrets.bat(gitignored) or host environment variables, never committed to git. Container configuration (names, paths) is in.env.container(gitignored). - User Management: Fixed UID (2000) ensures consistent file ownership across rebuilds, preventing git "dubious ownership" warnings while preserving security checks for legitimate cross-platform issues.
- Shells: Always attach via
exec -it -u devuser; no "original" shell that can kill the container. - Commands: Run from host terminal (PowerShell/Command Prompt) using batch scripts.
Create .env.container from .env.container.example:
IMAGE_NAME=my-dev-env
CONTAINER_NAME=my-dev-container
WORK_FOLDER=work
# Optional: Port to expose (e.g., 8080:8080 for web servers)
# EXPOSE_PORT=8080:8080
# Optional: Host folder to persist devuser home directory between rebuilds
# HOME_FOLDER=home
Note: You can set WORK_FOLDER=. to use the current directory as the work folder. This is useful when you want the Docker setup files in a subdirectory.
Note: Setting HOME_FOLDER allows the devuser's home directory (including shell history, installed user packages, and tool configurations) to persist across container rebuilds.
Set secrets using secrets.bat file (recommended):
- Copy
secrets.bat.exampletosecrets.bat - Fill in your actual secret values in
secrets.bat - Place
secrets.batin either:- The current folder (same directory as
rebuild.bat) - The parent folder (useful when
WORK_FOLDER=.to keep secrets outside the mounted volume)
- The current folder (same directory as
- The
rebuild.batscript will automatically search both locations and load the file when building
Alternatively, set environment variables in your PowerShell session (or system-wide):
$env:AMP_API_KEY = "sk-your-key"
$env:ANTHROPIC_API_KEY = "sk-ant-your-key"
$env:CURSOR_API_KEY = "your-key"
$env:GITHUB_TOKEN = "ghp-your-token"
$env:CLAUDE_CODE_OAUTH_TOKEN = "your-token"
$env:GIT_USER_NAME = "Your Name"
$env:GIT_USER_EMAIL = "your.email@example.com"
# Required if using GITHUB_TOKEN:
$env:GITHUB_USERNAME = "your-github-username"
$env:TZ = "America/New_York" # Override auto-detected timezoneNote: The secrets.bat file is gitignored and will never be committed. It's the recommended way to manage secrets as it's less error-prone than setting environment variables manually each time.
- Build and Create Container:
rebuild.bat
- Removes old image if it exists
- Builds a new image (no version tags)
- Creates (but doesn't start) the container
- Automatically detects Windows timezone and passes it to container
- Passes environment variables (API keys, git config, etc.) to container
- Creates work folder subdirectory if it doesn't exist
- Mounts work folder to
/home/devuser/workin container
- Open Shell:
cbash.bat
- Starts container if not running
- Opens interactive bash shell as
devuser(UID 2000) - Automatically stops container when last shell exits
- Multiple shells can be open simultaneously (container stays running)
Always installed:
- Git: Configured from environment variables
- Python 3: With pip
- Go: golang-go (via PPA for latest version)
- Node.js: v20.x LTS with npm
- Build tools: build-essential, curl, vim, tmux
- Document tools: pandoc, texlive-latex-recommended, texlive-fonts-recommended
Conditionally installed (only if corresponding API key is set during build):
- Amp CLI: AI coding agent - installed if
AMP_API_KEYis set - Claude Code: AI coding agent - installed if
CLAUDE_CODE_OAUTH_TOKENis set - Cursor agent: AI coding agent - installed if
CURSOR_API_KEYis set
- Git user: Configured from
GIT_USER_NAMEandGIT_USER_EMAILenvironment variables - GitHub authentication: Uses
GITHUB_TOKENandGITHUB_USERNAMEfor authenticated git operations - Timezone: Auto-detected from Windows, can be overridden with
TZenvironment variable
- Fixed UID: User
devuseralways has UID 2000 for consistent ownership - Windows mount fix: Entrypoint automatically fixes work directory ownership when owned by root (Windows mount artifact)
- Git security: Preserves git "dubious ownership" warnings for legitimate cross-platform issues while preventing false positives
- When to Update: Periodically (e.g., to refresh pip packages) or for major additions (e.g., new tools in Dockerfile).
- Steps:
- Edit
Dockerfileif adding/changing installs. - Rebuild:
- Edit
rebuild.bat
-
Automatically stops and removes old container
-
Removes old image
-
Builds new image
-
Creates new container from updated image
-
Work folder is preserved (not removed)
-
Optional Cleanup (if needed):
docker system prune # Prune dangling items (confirm prompt)
- List containers:
docker ps -a - List images:
docker images - Logs:
docker logs my-dev-container - Check container status:
docker inspect my-dev-container - If runtime changes need saving: Update Dockerfile instead of committing (avoid
docker commit). - Git "dubious ownership" warning: Should not occur with fixed UID. If it does, check ownership with
ls -ld /home/devuser/workandid. - Timezone issues: Check
TZenvironment variable or verify auto-detection inrebuild.bat.