Docker Deployment
Opstage CE is designed to run as a single container with a SQLite volume. This page covers the production-style deployment paths.
Public Review Docker path
During Public Review, the source-build Docker Compose path remains the canonical deployment path. GHCR images may be produced from main for validation, but the stable ghcr.io/xtrape-com/xtrape-capsule-ce:0.1.0 image becomes the primary documented path only after the v0.1.0 Public Preview cut. Snippets that reference ghcr.io/... below are for that post-cut pinned-image path.
Build and run from source (current path)
git clone https://github.com/xtrape-com/xtrape-capsule-ce.git
cd xtrape-capsule-ce
cp .env.example .env
# edit OPSTAGE_ADMIN_PASSWORD and OPSTAGE_SESSION_SECRET in .env
docker compose -f deploy/compose/docker-compose.yml up --build -dThe compose file builds the image locally, mounts a data volume, and exposes port 8080.
Single image (after v0.1.0 cut)
After the v0.1.0 Public Preview cut, use a pinned release tag. The expected shape is:
docker run -d \
--name opstage-ce \
-p 8080:8080 \
-v opstage-data:/app/data \
-e OPSTAGE_ADMIN_USERNAME="admin@example.local" \
-e OPSTAGE_ADMIN_PASSWORD="ChangeMeBeforeRunning123!" \
-e OPSTAGE_SESSION_SECRET="rotate-me" \
ghcr.io/xtrape-com/xtrape-capsule-ce:0.1.0Docker Compose against the pinned image
# docker-compose.yml
services:
opstage:
image: ghcr.io/xtrape-com/xtrape-capsule-ce:0.1.0
ports:
- "8080:8080"
environment:
OPSTAGE_ADMIN_USERNAME: "admin@example.local"
OPSTAGE_ADMIN_PASSWORD: "ChangeMeBeforeRunning123!"
OPSTAGE_SESSION_SECRET: "${OPSTAGE_SESSION_SECRET}"
OPSTAGE_AGENT_OFFLINE_THRESHOLD_SECONDS: "90"
OPSTAGE_AUDIT_RETENTION_DAYS: "90"
volumes:
- opstage-data:/app/data
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "/app/healthcheck.js"]
interval: 30s
timeout: 5s
retries: 3
volumes:
opstage-data:docker compose up -dData directory
| Path inside container | Contents |
|---|---|
/app/data/opstage.db | SQLite database |
/app/data/backups/ | (Optional) SQLite backups, when configured |
Mount a named volume or a host directory:
-v opstage-data:/app/data # named volume (recommended)
-v /opt/opstage:/app/data # bind mountCE is single-node. Running two backend containers against the
same SQLite file is unsupported. :::
Environment variables
The most relevant variables for a Docker deployment:
| Variable | Required | Default | Description |
|---|---|---|---|
OPSTAGE_ADMIN_USERNAME | bootstrap | — | Username for the bootstrap admin |
OPSTAGE_ADMIN_PASSWORD | bootstrap | — | Password for the bootstrap admin |
OPSTAGE_SESSION_SECRET | yes | — | Signs admin session cookies |
OPSTAGE_PORT | no | 8080 | HTTP listen port |
OPSTAGE_DATA_DIR | no | /app/data | Where SQLite + backups live |
OPSTAGE_AGENT_OFFLINE_THRESHOLD_SECONDS | no | 90 | Heartbeat-miss threshold |
OPSTAGE_SESSION_TTL_SECONDS | no | 28800 | Admin session TTL (8h) |
OPSTAGE_AUDIT_RETENTION_DAYS | no | 90 | Audit pruning window |
A complete reference is in Configuration.
Ports
A single HTTP port (default 8080) serves both the API (/api) and the static console.
If you put Opstage behind a reverse proxy:
- terminate TLS at the proxy,
- preserve cookies,
- preserve
X-CSRF-Token, - forward the original host (the UI builds same-origin URLs).
Logs
The backend logs to stdout/stderr in JSON-friendly form. Stream with:
docker logs -f opstage-ceBackups
For SQLite backup options (consistent online snapshots, owner-only download from the console, scheduled file copies), see Backup and Upgrade.
Upgrades
- Back up
/app/data. - Pull the new image:
docker pull ghcr.io/xtrape-com/xtrape-capsule-ce:<tag>. - Recreate the container:
docker compose up -d. - The backend runs Prisma migrations on start.
Pin a specific tag in your compose file. CE follows semver - read the
changelog before adopting a new minor. :::