307 lines
8.2 KiB
Markdown
307 lines
8.2 KiB
Markdown
# OpenCode Development Container
|
|
|
|
This repository provides a Podman-based development container for running an AI coding agent in an isolated and reproducible environment.
|
|
|
|
The container includes common software development tools, Node.js, OpenCode, Python, Java, Maven, Git, Neovim, and other command-line utilities.
|
|
|
|
The host project directory and selected development caches/configuration directories are mounted into the container.
|
|
|
|
## Requirements
|
|
|
|
* Linux
|
|
* Podman
|
|
* Bash
|
|
* A working internet connection when building the image
|
|
* CA certificates, if access to internal HTTPS services requires custom CAs
|
|
|
|
## Files
|
|
|
|
* `Dockerfile` — builds the development image.
|
|
* `build.sh` — builds, updates, removes, or prunes the image.
|
|
* `run.sh` — starts the development container.
|
|
* `certs/README.md` — rules for custom CA certificates.
|
|
|
|
## Build the image
|
|
|
|
Run:
|
|
|
|
```bash
|
|
./build.sh
|
|
```
|
|
|
|
The default image is:
|
|
|
|
```text
|
|
localhost/opencode-dev
|
|
```
|
|
|
|
The build uses the current host UID and GID for the `dev` user, so files created in mounted directories normally belong to the host user.
|
|
|
|
### Build without cache
|
|
|
|
```bash
|
|
./build.sh --no-cache
|
|
```
|
|
|
|
### Update the base image and Ubuntu packages
|
|
|
|
```bash
|
|
./build.sh --update
|
|
```
|
|
|
|
This is equivalent to `--no-cache --pull=newer`, so the Ubuntu base image is refreshed and every package layer is reinstalled.
|
|
|
|
### Show the build command without running it
|
|
|
|
```bash
|
|
./build.sh --dry-run
|
|
```
|
|
|
|
### Remove or prune images
|
|
|
|
```bash
|
|
./build.sh --remove # delete the development image
|
|
./build.sh --prune # delete dangling images left by previous builds
|
|
```
|
|
|
|
Both options exit immediately without building, and can be combined.
|
|
|
|
### Install extra system packages
|
|
|
|
```bash
|
|
./build.sh --apt-packages="libpq-dev postgresql-client"
|
|
```
|
|
|
|
The list is forwarded to the image build as `EXTRA_APT_PACKAGES` and installed with `apt-get install --no-install-recommends`.
|
|
|
|
### Show usage
|
|
|
|
```bash
|
|
./build.sh --help
|
|
```
|
|
|
|
## Build arguments
|
|
|
|
The following environment variables can be used to customize the image:
|
|
|
|
```bash
|
|
UBUNTU_VERSION=26.04
|
|
NODE_VERSION=24.21.0
|
|
OPENCODE_VERSION=2.0.22
|
|
IMAGE_REPO=localhost/opencode-dev
|
|
```
|
|
|
|
For example:
|
|
|
|
```bash
|
|
NODE_VERSION=24.21.0 OPENCODE_VERSION=2.0.22 ./build.sh --no-cache
|
|
```
|
|
|
|
`build.sh` passes the versions above to `podman build` as `--build-arg`. The same values are declared as `ARG` defaults at the top of the `Dockerfile`, so keep both in sync when changing a version.
|
|
|
|
Two more build arguments are set automatically:
|
|
|
|
* `DEV_UID` / `DEV_GID` — taken from `id -u` / `id -g` on the host.
|
|
* `EXTRA_APT_PACKAGES` — taken from the `--apt-packages` option.
|
|
|
|
## Custom CA certificates
|
|
|
|
Custom CA certificates must be placed in the repository `certs/` directory before building the image. See [`certs/README.md`](certs/README.md) for accepted formats and rules.
|
|
|
|
Git cannot track empty directories, so `certs/README.md` itself is the placeholder that keeps `certs/` present in a fresh clone, and the build does not fail on a missing directory. That placeholder is listed in `.dockerignore`, so only real certificate files are copied into the image.
|
|
|
|
The Dockerfile copies this directory to:
|
|
|
|
```text
|
|
/usr/local/share/ca-certificates
|
|
```
|
|
|
|
and runs:
|
|
|
|
```bash
|
|
update-ca-certificates
|
|
```
|
|
|
|
The resulting system CA bundle is also exposed through:
|
|
|
|
```text
|
|
NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
|
|
```
|
|
|
|
This allows both system tools and Node.js applications to trust the additional certificates.
|
|
|
|
**Important:** CA certificates need to be present when the image is built. The runtime container does not install certificates from the host. After adding or removing a certificate, rebuild with `./build.sh --no-cache --update`.
|
|
|
|
## Run the container
|
|
|
|
Run:
|
|
|
|
```bash
|
|
./run.sh
|
|
```
|
|
|
|
By default, the host directory is:
|
|
|
|
```text
|
|
$HOME/opencode-dev
|
|
```
|
|
|
|
and projects are stored in:
|
|
|
|
```text
|
|
$HOME/opencode-dev/projects
|
|
```
|
|
|
|
`run.sh` creates every directory it mounts, so it can be run on a clean checkout. If the image does not exist yet, the script exits with an error asking you to build it first.
|
|
|
|
The container starts in `/workspace`, which is the mounted `projects/` directory. Open the project you want to work on inside the shell, or mount additional host directories with `--mount`.
|
|
|
|
## Mounted directories
|
|
|
|
The following host directories are mounted into the container:
|
|
|
|
| Host | Container | Purpose |
|
|
| ------------------ | --------------------------------- | -------------------------------- |
|
|
| `projects/` | `/workspace` | Source code |
|
|
| `opencode-config/` | `/home/dev/.config/opencode` | OpenCode configuration |
|
|
| `opencode-data/` | `/home/dev/.local/share/opencode` | OpenCode persistent data |
|
|
| `opencode-cache/` | `/home/dev/.cache/opencode` | OpenCode cache |
|
|
| `m2/` | `/home/dev/.m2` | Maven cache |
|
|
| `npm-cache/` | `/home/dev/.npm` | npm cache |
|
|
| `pip-cache/` | `/home/dev/.cache/pip` | pip cache |
|
|
| `$HOME/.gitconfig` | `/home/dev/.gitconfig` | Git configuration |
|
|
|
|
All mounted development directories are created automatically by `run.sh`.
|
|
|
|
## Additional mounts
|
|
|
|
Additional host directories can be mounted with `--mount`.
|
|
|
|
Example:
|
|
|
|
```bash
|
|
./run.sh --mount "$HOME/other:/extra"
|
|
```
|
|
|
|
Read-only mounts are also supported:
|
|
|
|
```bash
|
|
./run.sh --mount "$HOME/data:/data:ro"
|
|
```
|
|
|
|
Multiple mounts can be specified:
|
|
|
|
```bash
|
|
./run.sh \
|
|
--mount "$HOME/data:/data:ro" \
|
|
--mount "$HOME/tools:/tools:rw"
|
|
```
|
|
|
|
`--mount` is the only option accepted by `run.sh`; any other argument stops the script with an error.
|
|
|
|
## Environment variables
|
|
|
|
The main runtime paths can be customized:
|
|
|
|
```bash
|
|
OPENCODE_DEV_HOME
|
|
PROJECT_DIR
|
|
OPENCODE_CONFIG
|
|
OPENCODE_SHARE_DATA
|
|
OPENCODE_CACHE
|
|
M2_FOLDER
|
|
NPM_CACHE
|
|
PIP_CACHE
|
|
IMAGE_REPO
|
|
```
|
|
|
|
`IMAGE_REPO` is read by both scripts, so it selects the same image for `build.sh` and `run.sh`.
|
|
|
|
Example:
|
|
|
|
```bash
|
|
OPENCODE_DEV_HOME="$HOME/my-opencode" ./run.sh
|
|
```
|
|
|
|
## Git configuration
|
|
|
|
The host Git configuration is mounted read-only:
|
|
|
|
```text
|
|
$HOME/.gitconfig -> /home/dev/.gitconfig
|
|
```
|
|
|
|
If the file does not exist, the script prints a warning and Git uses the container defaults.
|
|
|
|
SSH configuration and SSH agent forwarding are not mounted. If they are needed, pass them explicitly:
|
|
|
|
```bash
|
|
./run.sh --mount "$HOME/.ssh:/home/dev/.ssh:ro"
|
|
```
|
|
|
|
## Security
|
|
|
|
The container runs with several restrictions:
|
|
|
|
* Runs as the `dev` user with host UID/GID (`--user dev --userns=keep-id`).
|
|
* Drops all Linux capabilities (`--cap-drop=ALL`).
|
|
* Uses `no-new-privileges`.
|
|
* Uses the host timezone (`--tz=local`).
|
|
* Uses a temporary, named container (`--name opencode-dev --rm`).
|
|
* Does not run as root.
|
|
* Uses the default Podman network.
|
|
|
|
The project directory is mounted read/write because the AI agent needs to create and modify source files. The host Git configuration is mounted read-only.
|
|
|
|
## Locale
|
|
|
|
The image generates the `en_US.UTF-8` locale during the build:
|
|
|
|
```dockerfile
|
|
echo 'en_US.UTF-8 UTF-8' > /etc/locale.gen && locale-gen
|
|
```
|
|
|
|
The host timezone is applied at runtime through `--tz=local`. The host `LANG`/`LC_ALL` values are not forwarded; export them inside the container if a different locale is needed.
|
|
|
|
## Typical workflow
|
|
|
|
Build the image:
|
|
|
|
```bash
|
|
./build.sh
|
|
```
|
|
|
|
Start the development environment:
|
|
|
|
```bash
|
|
./run.sh
|
|
```
|
|
|
|
Inside the container, the projects directory is available under:
|
|
|
|
```bash
|
|
/workspace
|
|
```
|
|
|
|
Move into the project you want to work on, then start OpenCode normally:
|
|
|
|
```bash
|
|
opencode
|
|
```
|
|
|
|
The container is removed when the shell exits, while source code, OpenCode data, configuration, and development caches remain on the host.
|
|
|
|
## Customization
|
|
|
|
Image versions are defined at the top of the `Dockerfile`:
|
|
|
|
```dockerfile
|
|
ARG UBUNTU_VERSION=26.04
|
|
ARG NODE_VERSION=24.21.0
|
|
ARG OPENCODE_VERSION=2.0.22
|
|
```
|
|
|
|
Runtime paths, mounts, and container options are defined in `run.sh`, while `build.sh` controls how the image is built and maintained.
|
|
|
|
This separation allows the image to contain the stable development environment while projects, configuration, caches, and other working data remain outside the container.
|