All-in-one Docker container for OpenCode + OpenChamber web UI with encrypted API keys and TrueNAS Scale support
  • JavaScript 80.8%
  • Dockerfile 17.8%
  • Shell 1.4%
Find a file
crashmedia 1a709425d5
Some checks failed
Build and Push Docker Image / Build and Push (push) Failing after 2h48m20s
fix: increase blob upload timeout + add retry for EPIPE errors
- 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
2026-07-16 07:56:54 +00:00
.forgejo/workflows ci(build): use multi-tag oci-push instead of double push 2026-07-16 04:31:04 +00:00
actions fix: increase blob upload timeout + add retry for EPIPE errors 2026-07-16 07:56:54 +00:00
renovate chore(renovate): add Renovate dependency auto-update config 2026-05-21 04:08:13 +00:00
scripts fix: remove Arctic_Wolves-specific logic — generic infrastructure only 2026-05-30 19:12:13 +00:00
.dockerignore
.gitignore
commit-msg.txt
docker-compose.forgejo.yml
docker-compose.renovate.yml
docker-compose.yml fix: remove Arctic_Wolves-specific logic — generic infrastructure only 2026-05-30 19:12:07 +00:00
Dockerfile fix: set CARGO_HOME/RUSTUP_HOME to install Rust to correct directory 2026-07-16 01:31:33 +00:00
entrypoint.sh feat: monolithic Docker — PHP, MariaDB, PostgreSQL, nginx, supervisor, encrypted var fix, stale session cleanup 2026-05-30 17:51:48 +00:00
nginx-default.conf feat: monolithic Docker — PHP, MariaDB, PostgreSQL, nginx, supervisor, encrypted var fix, stale session cleanup 2026-05-30 17:52:03 +00:00
README.md docs: update README with SubStreamer + mediastack build tools 2026-07-16 01:36:17 +00:00
renovate.json
runner-config.yml
setup.js fix: preserve user config customizations during upstream sync 2026-07-16 00:47:29 +00:00
supervisord.conf fix: PostgreSQL startup — use explicit binary path, fix ownership, fix config override 2026-05-30 21:35:31 +00:00

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-sdk
  • JAVA_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)

  1. Go to Apps → Available Applications → Custom App
  2. Select Docker Compose
  3. Paste the docker-compose.yml content
  4. Fill in your environment variables (see reference table below)
  5. Set up persistent storage:
    • opencode_config/home/opencode/.config/opencode (Host Path or ixVolume)
    • opencode_projects/home/opencode/projects (Host Path or ixVolume)
  6. Deploy

Via Launch Docker Image

  1. Go to Apps → Available Applications → Launch Docker Image
  2. Image: git.crashmedia.ca/crashmedia/opencode-docker:latest
  3. Port Forwarding: Container Port 3000 → Node Port of your choice
  4. Environment Variables: Set all env vars from the reference table
  5. Storage:
    • Mount /home/opencode/.config/opencode to a Host Path
    • Mount /home/opencode/projects to a Host Path
  6. 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:

  1. Set OPENCODE_REPO (defaults to crashmedia/opencode)
  2. Set FORGEJO_TOKEN (needed for cloning)
  3. Start the containersetup.js clones 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.

  1. Open the OpenChamber web UI at http://your-host:3000
  2. Navigate to the settings/authentication panel
  3. Configure your OAuth provider (Google, GitHub, etc.)
  4. 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 .env file (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_TOKEN should be scoped to only the repos it needs
  • Rotate your API keys periodically
  • Never commit .env files 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_TOKEN and network access
  • Port conflict — change OPENCHAMBER_PORT or 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:

  1. Stop the container
  2. Clear the config volume
  3. 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


License

MIT