繁體中文版:README-zh.md
A customized code-server Docker image preloaded with developer tooling and AI coding assistants. Intended for homelab and remote-development setups where you want a single browser-accessible VS Code environment with AI agents already wired in.
Built on codercom/code-server:4.126.0 (Debian-based). Published as a multi-arch image for linux/amd64 and linux/arm64.
| Tool | Source | Notes |
|---|---|---|
| Claude Code | Official Anthropic apt repository | Native binary at /usr/bin/claude. Auto-update disabled; updates arrive via image rebuild. |
| OpenAI Codex CLI | npm (@openai/codex) |
Installed user-scope under /home/coder/.npm-global/ so coder can npm update it without root. See sandbox note below. |
Google Antigravity CLI (agy) |
Official install script | Installed to /home/coder/.local/bin/. |
On container start the entrypoint launches codex remote-control in the background, supervised by a restart loop (re-spawns 5s after any exit). This lets you drive the in-container Codex agent remotely without manually attaching a shell.
- Logs stream to
/home/coder/.codex/remote-control.log. - Disable it by setting
ENABLE_CODEX_REMOTE=0. - Skipped automatically if the
codexbinary is not onPATH.
bubblewrap is installed and /usr/bin/bwrap is configured setuid-root (chmod 4750, owned by the bwrap-users group, with coder added to it). This gives Codex CLI a working command-execution sandbox inside the container without granting coder broad root access.
gcc,g++,make(viabuild-essential)cmake,clangd— for C/C++ work alongside the bundled extensions- Python 3 with
pipandvenv - Node.js 22 LTS with
npm - uv — Astral’s Python package and project manager, installed to
/usr/local/bin/
- LibreOffice — full office suite, usable headlessly via
unoserver python3-uno— Python–UNO bridge for LibreOffice scriptingunoserver— exposes LibreOffice over a network socket for programmatic document conversion
tmux— terminal multiplexerzip,unzip,p7zip-full— archive creation and extraction
Installed so AI agents can manipulate Office documents programmatically without per-project venv setup:
python-pptxpython-docxopenpyxlpgcliunoserver
psql(PostgreSQL client)pgcli(PostgreSQL CLI with autocomplete and syntax highlighting)
@playwright/cli — Microsoft's agent-oriented Playwright CLI (command playwright-cli). It exposes a token-efficient command interface purpose-built for coding agents to drive a real browser (an alternative to the Playwright MCP).
- CLI — installed globally via npm under
/home/coder/.npm-global/(commandplaywright-cli). Headless by default; works as the non-root container user with no extra flags. - Chromium — pre-downloaded at build time into
/ms-playwright(PLAYWRIGHT_BROWSERS_PATH), readable regardless of the runtime UID. System libraries come from Playwright's owninstall-deps. - Agent skills — on first container start the entrypoint installs the
playwright-cliskill into each agent's global skills directory (so all three discover it, and the files persist on the bind-mounts):- Claude Code →
~/.claude/skills/ - Codex →
~/.codex/skills/ - Antigravity →
~/.gemini/antigravity-cli/skills/
- Claude Code →
Usage (your agent runs these; you can too):
playwright-cli open https://demo.playwright.dev/todomvc/
playwright-cli type "Buy groceries"
playwright-cli press Enter
playwright-cli snapshot # capture element refs
playwright-cli screenshot --filename shot.png
playwright-cli closeRun playwright-cli --help for the full command list, or just point your agent at the CLI and let it read the installed skill. Chromium needs a large /dev/shm; the supplied compose.yml sets shm_size: "1gb" and init: true.
Sourced from Open VSX:
| Extension | ID |
|---|---|
| clangd | llvm-vs-code-extensions.vscode-clangd |
| Code Runner | formulahendry.code-runner |
| VSCode Counter | uctakeoff.vscode-counter |
| Prettier | esbenp.prettier-vscode |
| CMake | twxs.cmake |
| CMake Tools | ms-vscode.cmake-tools |
| markdownlint | DavidAnson.vscode-markdownlint |
| Ruff | charliermarsh.ruff |
| basedpyright | detachhead.basedpyright |
| uv Toolkit | the0807.uv-toolkit |
| Jupyter | ms-toolsai.jupyter |
| Claude Code | Anthropic.claude-code |
| Codex | openai.chatgpt |
Python tooling uses Ruff (lint/format) and basedpyright (type checking) instead of the Microsoft Python extension; the build also uninstalls ms-python.vscode-python-envs to avoid env-picker conflicts with uv.
Extensions are baked into /opt/extensions-seed/ at build time. On first container start, a small entrypoint wrapper seeds them into the user’s extensions directory so they survive bind-mount overlays and remain user-editable at runtime.
Published to GitHub Container Registry on every push to main and on v* tags:
ghcr.io/allen5218/agent-codeserver:<tags>
Available tags:
latest— currentmainbranch<short-sha>— every commitYYYY-MM-DD— date-stamped snapshots frommainv*— release tags
Each tag is a multi-arch manifest covering linux/amd64 and linux/arm64; docker pull automatically selects the right architecture. CI builds each architecture natively (amd64 on ubuntu-latest, arm64 on ubuntu-24.04-arm) and merges them into a single manifest.
The repo ships a compose.yml running two services: code-server (this image) and a cloudflared tunnel that exposes it without opening any inbound ports. The image is multi-arch: the same compose.yml works on both linux/amd64 and linux/arm64 because it is pinned to a multi-arch manifest-list digest, and Docker pulls the matching architecture automatically.
Clone the repo (or just copy compose.yml and .env.example out of it — those two files are all you need to deploy):
git clone https://github.com/allen5218/agent-codeserver.git
cd agent-codeserverRun every remaining step from this directory — it holds both compose.yml and .env.example, and is where docker compose looks for them.
These are the bind-mount targets referenced by compose.yml. They live under /opt/docker-stacks/vscode, separate from the cloned repo. Create them and hand them to the UID:GID the container will run as (must match APP_UID/APP_GID in .env):
# Create the bind-mount targets (matching the volumes in compose.yml)
sudo mkdir -p /opt/docker-stacks/vscode/{data,projects,codex,gemini,claude}
# Claude Code needs an existing JSON file to mount
echo '{}' | sudo tee /opt/docker-stacks/vscode/claude.json > /dev/null
# chown everything to the UID:GID the container runs as (1000:1000 here; match .env)
sudo chown -R 1000:1000 /opt/docker-stacks/vscode
~/.local(Playwright, Antigravity) and~/.npm-global(Codex) are deliberately not mounted — these CLIs are baked into the image at build time, and overlaying an empty host directory would shadow them. Only mount~/.npm-globalif you want to persist a Codex that younpm updateyourself (see Suggested persistent paths below).
From the cloned repo (where .env.example lives):
cp .env.example .env
# Edit .env: set CODE_PASSWORD, APP_UID/APP_GID, CF_TUNNEL_TOKENSee .env.example for the full list.
- Zero Trust dashboard → Networks → Tunnels → Create a tunnel
- Choose Cloudflared and name it (e.g.
homelab) - Copy token — this is
CF_TUNNEL_TOKENin.env - On the Public Hostname tab, add an entry:
- Subdomain:
code - Domain:
your.dev - Type:
HTTP - URL:
code-server:8080(the compose service name + in-container port, resolved on the shared network)
- Subdomain:
docker compose up -dOpen https://code.your.dev and log in with CODE_PASSWORD.
| Variable | Required | Purpose |
|---|---|---|
PASSWORD |
Yes | code-server login password |
XDG_DATA_HOME |
Pre-set to /home/coder/.config |
Consolidates code-server config and extensions into a single mount |
ENABLE_CODEX_REMOTE |
No (defaults to 1) |
Set to 0 to skip auto-starting the background codex remote-control loop |
The table above lists container-side variables. When deploying with compose.yml, the host-side values come from .env: CODE_PASSWORD (mapped to PASSWORD), APP_UID/APP_GID (container run-as identity), and CF_TUNNEL_TOKEN (Cloudflare tunnel). See .env.example.
Mount these as volumes to preserve state across container restarts:
| Container path | Holds |
|---|---|
/home/coder/.config |
code-server settings, keybindings, and installed extensions |
/home/coder/project |
Workspace / working directory |
/home/coder/.claude + /home/coder/.claude.json |
Claude Code state and OAuth token |
/home/coder/.codex |
OpenAI Codex auth, sessions, history, and remote-control log |
/home/coder/.gemini |
Antigravity tasks, MCP configs, and rules |
/home/coder/.npm-global |
User-scope npm prefix; persist to keep npm updated Codex |
The Antigravity CLI’s own settings (~/.config/antigravity/, ~/.config/agy/) are covered by the ~/.config mount automatically.
The image entrypoint is /usr/local/bin/seed-and-run.sh:
- On first run, copies baked extensions from
/opt/extensions-seed/into$XDG_DATA_HOME/code-server/extensions/if that directory is empty. - Unless
ENABLE_CODEX_REMOTE=0, startscodex remote-controlin the background under a supervisor loop (restarts 5s after any exit), logging to/home/coder/.codex/remote-control.log. - Delegates to the original
codercom/code-serverentrypoint, which handlesfixuidand launchescode-server.
agent-docs/ contains reference sheets for AI agents running inside the container:
| File | Read by |
|---|---|
agent-docs/CLAUDE.md |
Claude Code (point it at the file with @agent-docs/CLAUDE.md) |
agent-docs/AGENTS.md |
OpenAI Codex and other agents that honour AGENTS.md |
Both files document the pre-installed tools, library locations, and example commands available in the container.
The image builds automatically via GitHub Actions:
- Pushes to
main,v*tags, and changes toDockerfile,entrypoint.sh, or the workflow trigger a build. - Renovate keeps the
codercom/code-serverbase image, GitHub Action versions, and image digests current.
To build locally:
docker build -t agent-codeserver:dev .MIT — see LICENSE.
Bundled software retains its own license; this repository’s license applies only to the configuration, Dockerfile, and entrypoint script in this repo.