Skip to content

Docker Setup

Kazier’s backend is a single Go binary (Tesseract embedded for OCR) and its database is PostgreSQL 17. The repository ships a docker-compose.yml that runs the whole stack: the database and the backend API.

ServiceImagePurpose
postgrespostgres:17-alpineMetadata database
backendbuilt from ./backend (Dockerfile)Go API (image also published to Docker Hub)

The published image is docker.io/thirdshop/kasier (tagged vX.Y.Z + latest).

The paddleocr service seen in the historical file was rejected in favor of Tesseract, which the backend calls directly as a system command.

  • Docker with Compose V2
  • mandatory environment variables (see Environment Variables): AUTH_SECRET, ADMIN_USERNAME, ADMIN_PASSWORD
services:
postgres:
image: postgres:17-alpine
ports:
- "5432:5432"
environment:
POSTGRES_DB: ${POSTGRES_DB:-kazier_dev}
POSTGRES_USER: ${POSTGRES_USER:-kazier}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-kazier}
volumes:
- postgres-data:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-kazier}"]
interval: 10s
timeout: 5s
retries: 5
backend:
build:
context: ./backend
dockerfile: Dockerfile
args:
VERSION: ${VERSION:-dev}
ports:
- "${PORT:-8080}:8080"
environment:
PORT: 8080
DATABASE_URL: postgres://${POSTGRES_USER:-kazier}:${POSTGRES_PASSWORD:-kazier}@postgres:5432/${POSTGRES_DB:-kazier_dev}?sslmode=disable
UPLOAD_DIR: /app/uploads
MAX_FILE_SIZE_MB: ${MAX_FILE_SIZE_MB:-50}
OCR_LANG: ${OCR_LANG:-fra+eng}
AUTH_SECRET: ${AUTH_SECRET:?AUTH_SECRET required}
ADMIN_USERNAME: ${ADMIN_USERNAME:?ADMIN_USERNAME required}
ADMIN_PASSWORD: ${ADMIN_PASSWORD:?ADMIN_PASSWORD required}
volumes:
- backend-uploads:/app/uploads
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
volumes:
postgres-data:
backend-uploads:
Terminal window
# Copy the example env and edit the secrets
cp backend/.env.example .env
# Start the whole stack (waits for the database healthcheck)
docker compose up -d --build
# Health check
curl http://localhost:8080/api/v1/health
# → {"data":{"status":"healthy"}}
# Follow logs
docker compose logs -f
# Stop the stack
docker compose down
# Stop and delete the data volume (⚠️ destroys metadata)
docker compose down -v

Rotate POSTGRES_PASSWORD, AUTH_SECRET and the admin credentials outside of the compose file (e.g. your .env, Docker secrets). Never use the defaults in production.

VariableDefaultDescription
POSTGRES_DBkazier_devDatabase created at first boot
POSTGRES_USERkazierDatabase user
POSTGRES_PASSWORDkazierPassword (change for production)
VERSIONdevBuild arg injected into main.version (ldflags)

Backend variables are documented in Environment Variables.

The backend image is built by a multi-stage Dockerfile:

  • Builder (golang:1.26-alpine): compiles a static binary with CGO_ENABLED=0 and the version baked in (ldflags).
  • Runtime (alpine, non-root user): ships Tesseract with English and French language data (fra+eng, default OCR_LANG), ca-certificates, a persistent UPLOAD_DIR at /app/uploads, a Docker HEALTHCHECK, and OCI labels (AGPL-3.0-only, source URL, version).

The server listens on all interfaces (:PORT, default 8080) — no HOST variable is needed.

To run the published image instead of building it locally:

Terminal window
docker run --rm \
-e AUTH_SECRET="$AUTH_SECRET" \
-e ADMIN_USERNAME="$ADMIN_USERNAME" \
-e ADMIN_PASSWORD="$ADMIN_PASSWORD" \
-e DATABASE_URL="postgres://kazier:kazier@host.docker.internal:5432/kazier_dev?sslmode=disable" \
-p 8080:8080 \
thirdshop/kasier:latest