- JavaScript 80.8%
- Dockerfile 17.8%
- Shell 1.4%
|
Some checks failed
Build and Push Docker Image / Build and Push (push) Failing after 2h48m20s
- Blob uploads now use 10min timeout (or proportional to size) - Failed uploads retry up to 3 times with fresh connections - Handles EPIPE, ECONNRESET, ETIMEDOUT, and socket hang up errors |
||
|---|---|---|
| .forgejo/workflows | ||
| actions | ||
| renovate | ||
| scripts | ||
| .dockerignore | ||
| .gitignore | ||
| commit-msg.txt | ||
| docker-compose.forgejo.yml | ||
| docker-compose.renovate.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| entrypoint.sh | ||
| nginx-default.conf | ||
| README.md | ||
| renovate.json | ||
| runner-config.yml | ||
| setup.js | ||
| supervisord.conf | ||
OpenCode + OpenChamber — All-in-One Docker Container
Run OpenCode AI agent platform with OpenChamber web UI in a single container. Includes pre-installed build tools for SubStreamer (Android APK) and Mediastack/CrashFin (Rust + Flatpak) projects.
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Docker Container │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ entrypoint.sh → setup.js → openchamber serve │ │
│ │ │ │
│ │ • Set plaintext env vars directly │ │
│ │ • Install additional tools (APK/PIP/NPM) │ │
│ │ • Clone config repo from Forgejo │ │
│ │ • Generate opencode.json from env vars │ │
│ │ • Drop privileges to opencode user (UID 568) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ OpenCode │ │ OpenChamber │ │
│ │ (SDK) │◄──►│ Web UI :3000 │ │
│ └──────────────┘ └──────────────────┘ │
│ │
│ Runtime: │
│ PHP 8.3 │ MariaDB │ PostgreSQL 16 │ nginx │ Node.js 22 │
│ │
│ Build Tools: │
│ Rust 1.97 │ JDK 17 │ Android SDK 34 │ CMake │ Meson │
│ Flatpak Builder │ GCC │ G++ │ Make │ Ninja │
│ │
│ Volumes: │
│ /home/opencode/.config/opencode/ — Config (agents, etc.) │
│ /home/opencode/projects/ — Temp git projects │
└─────────────────────────────────────────────────────────────┘
Pre-Installed Build Tools
The container includes build tools for two first-party projects, so you can build APKs and Flatpak packages directly in the container without installing anything extra.
SubStreamer (Android APK)
Tools for building the SubStreamer React Native/Expo Android app:
| Tool | Version | Purpose |
|---|---|---|
| OpenJDK | 17 | Android compilation |
| Android SDK Platform | 34 | Target API level |
| Android Build Tools | 34.0.0 | APK packaging |
| Android Platform Tools | 37.0.0 | ADB and utilities |
| Node.js | 22 | Expo/RN build toolchain |
Environment variables are pre-configured:
ANDROID_HOME=/opt/android-sdkJAVA_HOME=/usr/lib/jvm/java-17-openjdk
Mediastack / CrashFin (Rust + Flatpak)
Tools for building the CrashFin media center application:
| Tool | Version | Purpose |
|---|---|---|
| Rust | 1.97 (stable) | Primary language |
| Cargo | Bundled | Rust package manager |
| musl target | x86_64-unknown-linux-musl | Static binary builds |
| GCC / G++ | 14.2 | C/C++ compilation |
| CMake | 3.31 | Build system |
| Meson | 1.6 | Build system (mpv) |
| Ninja | 1.2 | Build backend |
| pkgconf | 2.3 | Library discovery |
Development libraries:
| Library | Purpose |
|---|---|
| openssl-dev | TLS/SSL |
| wayland-dev | Display protocol |
| libxkbcommon-dev | Keyboard handling |
| libva-dev | Hardware video decoding |
| vulkan-loader-dev | GPU acceleration |
| pulseaudio-dev | Audio output |
| dbus-dev | System integration |
| nftables | Firewall (VPN split tunneling) |
Flatpak packaging:
| Tool | Details |
|---|---|
| flatpak | Runtime and package management |
| flatpak-builder | Build Flatpak packages |
| Flathub remote | Pre-configured |
| KDE Platform 6.8 | Pre-installed runtime |
| KDE SDK 6.8 | Pre-installed SDK |
Quick Start
1. Pull and Run
docker login git.crashmedia.ca -u crashmedia
# (enter your FORGEJO_TOKEN as password)
docker run -d \
--name opencode \
-p 3000:3000 \
-e OPENAI_API_KEY=sk-proj-your-key \
-e FORGEJO_TOKEN=your-token \
-e FORGEJO_URL=https://git.crashmedia.ca \
-v opencode_config:/home/opencode/.config/opencode \
-v opencode_projects:/home/opencode/projects \
git.crashmedia.ca/crashmedia/opencode-docker:latest
2. Open the UI
Navigate to http://localhost:3000
Docker Compose
version: '3.8'
services:
opencode:
image: git.crashmedia.ca/crashmedia/opencode-docker:latest
container_name: opencode
restart: unless-stopped
ports:
- "3000:3000"
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
- GEMINI_API_KEY=${GEMINI_API_KEY:-}
- FORGEJO_TOKEN=${FORGEJO_TOKEN:-}
- FORGEJO_URL=${FORGEJO_URL:-https://git.crashmedia.ca}
- OPENCODE_REPO=${OPENCODE_REPO:-crashmedia/opencode}
volumes:
- opencode_config:/home/opencode/.config/opencode
- opencode_projects:/home/opencode/projects
volumes:
opencode_config:
driver: local
opencode_projects:
driver: local
Create a .env file:
OPENAI_API_KEY=sk-proj-your-key-here
ANTHROPIC_API_KEY=sk-ant-your-key-here
GEMINI_API_KEY=your-gemini-key-here
FORGEJO_TOKEN=your-forgejo-token-here
FORGEJO_URL=https://git.crashmedia.ca
Then run:
docker compose up -d
TrueNAS Scale Installation
Prerequisites
- TrueNAS Scale 24.10 (Electric Eel) or later
- Docker image pulled to your local registry or accessible via Forgejo
Via Custom App (Docker Compose)
- Go to Apps → Available Applications → Custom App
- Select Docker Compose
- Paste the
docker-compose.ymlcontent - Fill in your environment variables (see reference table below)
- Set up persistent storage:
opencode_config→/home/opencode/.config/opencode(Host Path or ixVolume)opencode_projects→/home/opencode/projects(Host Path or ixVolume)
- Deploy
Via Launch Docker Image
- Go to Apps → Available Applications → Launch Docker Image
- Image:
git.crashmedia.ca/crashmedia/opencode-docker:latest - Port Forwarding: Container Port
3000→ Node Port of your choice - Environment Variables: Set all env vars from the reference table
- Storage:
- Mount
/home/opencode/.config/opencodeto a Host Path - Mount
/home/opencode/projectsto a Host Path
- Mount
- Deploy
Environment Variables Reference
AI Provider API Keys
Set these directly as environment variables:
| Variable | Description |
|---|---|
OPENAI_API_KEY |
OpenAI / ChatGPT API key |
ANTHROPIC_API_KEY |
Anthropic / Claude API key |
GEMINI_API_KEY |
Google Gemini API key |
AZURE_OPENAI_API_KEY |
Azure OpenAI API key |
OPENROUTER_API_KEY |
OpenRouter API key |
GROQ_API_KEY |
Groq API key |
DEEPSEEK_API_KEY |
DeepSeek API key |
ZAI_API_KEY |
Z.AI API key |
MISTRAL_API_KEY |
Mistral AI key |
TOGETHER_API_KEY |
Together AI key |
Forgejo Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
FORGEJO_URL |
No | https://git.crashmedia.ca |
Base URL of your Forgejo instance |
FORGEJO_TOKEN |
Recommended | - | Forgejo personal access token for repo operations |
OPENCODE_REPO |
No | crashmedia/opencode |
OpenCode config repository (format: owner/repo). Cloned to config dir on first run if empty. |
OAuth Authentication
OAuth is configured and authenticated through the OpenChamber web UI — no environment variables needed. Auth sessions are persisted to the OpenChamber config directory on the persistent volume. Navigate to the web UI and use the built-in OAuth/settings panel to configure providers.
OpenChamber Web UI
| Variable | Required | Default | Description |
|---|---|---|---|
OPENCHAMBER_PORT |
No | 3000 |
Port for the web UI |
UI_PASSWORD |
No | — | Password-protect the web UI (also supports OPENCHAMBER_UI_PASSWORD) |
Additional Tool Installation
Tools are installed at container startup before OpenChamber starts.
| Variable | Required | Default | Description |
|---|---|---|---|
APK_PACKAGES |
No | — | Space-separated Alpine packages (e.g., vim htop tmux) |
PIP_PACKAGES |
No | — | Space-separated Python packages (e.g., pandas numpy jupyter) |
NPM_PACKAGES |
No | — | Space-separated global npm packages (e.g., typescript prettier eslint) |
OpenCode Plugins
| Variable | Required | Default | Description |
|---|---|---|---|
OPENCODE_PLUGINS |
No | — | Comma-separated OpenCode plugins (e.g., opencode-gemini-auth,opencode-synced) |
Persistent Volumes
Named Volumes vs Bind Mounts
There are two ways to persist data. Choose ONE approach:
Option A: Named Volumes (default docker-compose.yml)
volumes:
- opencode_config:/home/opencode/.config
- opencode_projects:/home/opencode/projects
- opencode_state:/home/opencode/.local/share/opencode
- Simple, works out of the box
- Docker auto-initializes with content from the image
- Data stored in Docker's internal area (
/var/lib/docker/volumes/) — NOT visible at a host path - Best for: testing, local Docker Desktop, quick setup
Option B: Bind Mounts (RECOMMENDED for TrueNAS Scale)
volumes:
- /mnt/pool/opencode/config:/home/opencode/.config
- /mnt/pool/opencode/projects:/home/opencode/projects
- /mnt/pool/opencode/state:/home/opencode/.local/share/opencode
- Data visible directly on your host/pool filesystem
- Easy to inspect, back up, or modify files
- Starts EMPTY — Docker does NOT copy initial image content into bind mounts
- Best for: TrueNAS Scale, production, any setup where you need to access the files
How to Populate a Bind Mount (First Time)
Since bind mounts start empty, the container needs to clone the config repo on first startup. The setup.js script handles this automatically:
- Set
OPENCODE_REPO(defaults tocrashmedia/opencode) - Set
FORGEJO_TOKEN(needed for cloning) - Start the container —
setup.jsclones the repo into the empty bind mount
If the bind mount is empty and the clone doesn't trigger automatically, add:
environment:
- INIT_CONFIG=true
This forces a fresh clone even if the config directory exists (it backs up the old config first).
Container Paths Reference
| Container Path | Purpose | Contains |
|---|---|---|
/home/opencode/.config |
All configurations (auto-detected) | opencode/ (agents, skills, commands, tools) + openchamber/ (auth sessions, settings) |
/home/opencode/projects |
Workspace for git/project operations | Cloned repos, agent working directories |
/home/opencode/.local/share/opencode |
OpenCode runtime state | Sessions, cache, persisted auth |
OpenCode automatically finds its config at ~/.config/opencode/.
OpenChamber automatically finds its config at ~/.config/openchamber/.
Config Persistence
User customizations to opencode.json (theme, model selection, provider keys, etc.) are preserved across container restarts. When the upstream config repo changes, the sync process backs up your opencode.json and restores it after applying upstream updates.
To force a complete re-sync from upstream:
environment:
- REFRESH_CONFIG=true # Overwrites tools, skills, agents with latest from repo
Or for a completely clean slate:
environment:
- INIT_CONFIG=true # Backs up and re-clones everything
Both env vars can be removed after the restart completes.
Using OAuth Authentication
OAuth is configured and authenticated directly through the OpenChamber web UI — no environment variables required.
- Open the OpenChamber web UI at
http://your-host:3000 - Navigate to the settings/authentication panel
- Configure your OAuth provider (Google, GitHub, etc.)
- Auth sessions are automatically persisted to the persistent volume
The sessions and tokens are stored in OpenChamber's SQLite database under /home/opencode/.config/openchamber/, which lives on the persistent config volume. This means your authentication survives container restarts.
Installing Additional Tools, Agents, Skills
At Container Startup (via env vars)
environment:
- APK_PACKAGES=vim git-lfs
- NPM_PACKAGES=@anthropic-ai/sdk openai
- PIP_PACKAGES=pillow matplotlib
Persistently (via config volume)
The mounted config volume at /home/opencode/.config/opencode is your OpenCode source-of-truth. Add agents, skills, and commands directly to this directory:
# Example: add a new agent
mkdir -p /mnt/pool/opencode/config/agents
cp my-custom-agent.md /mnt/pool/opencode/config/agents/
# Example: add a new skill
mkdir -p /mnt/pool/opencode/config/skills/my-skill
cp SKILL.md /mnt/pool/opencode/config/skills/my-skill/
Changes take effect after restart (OpenCode re-reads the config on startup).
Building the Image Yourself
# Clone this repo
git clone https://git.crashmedia.ca/crashmedia/opencode-docker.git
cd opencode-docker
# Build
docker build -t opencode-docker:latest .
# Tag for Forgejo
docker tag opencode-docker:latest \
git.crashmedia.ca/crashmedia/opencode-docker:latest
# Push to Forgejo
docker login git.crashmedia.ca -u crashmedia
docker push git.crashmedia.ca/crashmedia/opencode-docker:latest
# Multi-arch build (for amd64 + arm64)
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t git.crashmedia.ca/crashmedia/opencode-docker:latest \
--push .
Health Check
The container includes a built-in health check that pings http://localhost:3000/ every 30 seconds. In TrueNAS Scale, you can monitor the container health status in the Apps dashboard.
Security Notes
Environment variables are set directly in your TrueNAS Scale UI, Docker Compose file, or .env file. These platforms already provide their own access controls:
- TrueNAS Scale: Environment variables are stored in the system database and only accessible to users with app management permissions.
- Docker Compose / .env files: Restrict file permissions on your
.envfile (chmod 600 .env) and ensure your host system is properly secured. - Named volumes: Data in Docker volumes is only accessible to users who can run Docker commands.
Best Practices
- Use fine-grained API tokens with minimal required permissions
- The
FORGEJO_TOKENshould be scoped to only the repos it needs - Rotate your API keys periodically
- Never commit
.envfiles or plaintext secrets to version control
Troubleshooting
Container exits immediately
Check the container logs:
docker logs opencode
Common causes:
- Wrong or missing
FORGEJO_TOKEN- git clone fails - Git clone fails — check
FORGEJO_TOKENand network access - Port conflict — change
OPENCHAMBER_PORTor host mapping
OpenChamber starts but OpenCode doesn't respond
Ensure your API keys are correctly set. OpenChamber needs at least one provider configured to start OpenCode. Check:
docker exec opencode env | grep -E 'API_KEY|OAUTH'
Config volume is empty after restart
The config repo clones only on first run when the config directory is empty. If you want to re-clone:
- Stop the container
- Clear the config volume
- Restart
I want to update agents/skills without restart
Push changes to your Forgejo config repo. The mounted config volume will sync when you restart the container.
Repository
- Source: https://git.crashmedia.ca/crashmedia/opencode-docker
- Container:
git.crashmedia.ca/crashmedia/opencode-docker:latest - Config Repo: https://git.crashmedia.ca/crashmedia/opencode
License
MIT