Docker Deployment
Vetrix ships as a minimal Alpine-based Docker image. This guide covers single-server deployment using Docker Compose.
Prerequisites
- Docker 24+ with BuildKit
- Docker Compose v2 (
docker compose, notdocker-compose) - A domain name (or use
localhostfor local testing)
Quick start (local)
# 1. Clone the repository or download a release tarball
git clone https://github.com/your-org/vetrix
cd vetrix
# 2. Generate secrets
export JWT_SECRET=$(openssl rand -hex 32)
export POSTGRES_PASSWORD=$(openssl rand -hex 24)
export SECRET_ENC_KEY=$(openssl rand -hex 32)
# 3. Start services
docker compose up -d
# 4. Wait for readiness
until curl -sf http://localhost:3000/api/v1/ready; do sleep 2; done
# 5. Create the admin account (first-run wizard)
curl -X POST http://localhost:3000/api/v1/setup \
-H 'Content-Type: application/json' \
-d '{"username":"admin","email":"admin@example.com","password":"CHANGE_ME"}'
Automated single-server install
The scripts/install.sh script handles Docker installation, secret generation, systemd unit creation, and first-run in one step:
curl -fsSL https://raw.githubusercontent.com/your-org/vetrix/main/scripts/install.sh \
| VETRIX_DOMAIN=git.example.com bash
Environment variables
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL DSN, e.g. postgres://vetrix:pass@db:5432/vetrix?sslmode=disable |
JWT_SECRET |
Yes | ≥32-char random string for token signing |
SECRET_ENC_KEY |
Required once any webhook / encrypted backup / admin secret is stored | 64-char hex string (32 bytes) for encrypting credentials at rest. Startup refuses to boot when this is unset or malformed and any dependent row exists; a brand-new install may boot without it (with a loud WARN) but will not accept new secrets until it is configured. |
POSTGRES_PASSWORD |
Yes (Compose) | PostgreSQL password (used by the db service) |
SERVER_PORT |
No | Override HTTP port (default: 3000) |
SSH_PORT |
No | Override SSH port (default: 2222) |
GIT_REPO_ROOT |
No | Path for bare repositories (default: /data/repositories) |
Volumes
| Mount | Purpose |
|---|---|
/data/repositories |
Bare git repositories |
/data/ssh |
SSH host key (auto-generated on first start) |
Mount a persistent volume at /data to survive container restarts.
Building the image
# Build server image (default target)
docker build -t vetrix:latest .
# Build worker image (CI/CD runner)
docker build --target worker -t vetrix-worker:latest .
# Multi-arch build
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t vetrix:latest \
--push .
Upgrading
Vetrix applies pending database migrations automatically on startup. To upgrade:
docker compose pull
docker compose up -d
The server will run migrations before accepting traffic. The readiness probe at /api/v1/ready returns 503 until migrations complete.
Health probes
| Endpoint | Type | Returns |
|---|---|---|
GET /api/v1/health |
Liveness | 200 always |
GET /api/v1/ready |
Readiness | 200 when DB reachable, 503 otherwise |
Production hardening checklist
- Set
external_urlinapp.tomlto your public HTTPS URL - Terminate TLS in front of Vetrix (nginx, Traefik, or a load balancer)
- Restrict port 3000 to the reverse proxy; do not expose it publicly
- Use a dedicated PostgreSQL instance (not the Compose
dbservice) for production - Back up
/data/repositoriesand the PostgreSQL database on a schedule - Rotate
JWT_SECRETperiodically (invalidates existing tokens — users must re-login)