Skip to content

Production Deployment

Container Images

Images are published to the Forgejo container registry via manual workflow dispatch.

Registry: git.lan.raz.wtf/raz/libris

ImagePortDescription
git.lan.raz.wtf/raz/libris/api3000Nitro API server
git.lan.raz.wtf/raz/libris/web3100Nuxt frontend (SSR)

Tags:

  • v<version> — version from package.json (e.g., v1.2.0)
  • latest — most recent build

Building images

Trigger the Publish Images workflow from the Forgejo UI: Actions > Publish Images > Run workflow. It builds both images from the Dockerfile.api and Dockerfile.web at the current HEAD.

Pulling images

bash
docker login git.lan.raz.wtf
docker pull git.lan.raz.wtf/raz/libris/api:latest
docker pull git.lan.raz.wtf/raz/libris/web:latest

Environment Variables

API service (required)

VariablePurpose
NITRO_DATABASE_URLPostgreSQL connection string (e.g., postgresql://user:pass@host:5432/libris)
NITRO_REDIS_URLRedis connection string (e.g., redis://host:6379)
NITRO_INBOX_PATHWritable directory for uploaded book files
NITRO_LIBRARY_PATHWritable directory for organized book storage
NITRO_API_SECRET_KEYToken encryption secret — minimum 32 characters

API service (optional)

VariablePurpose
NITRO_CORS_ORIGINAllowed CORS origins (comma-separated or *)
NITRO_KOSYNC_USERNAMEKoReader sync username
NITRO_KOSYNC_PASSWORD_HASHbcrypt hash of KoSync password

Web service (required)

VariablePurpose
NUXT_SESSION_PASSWORDSession cookie encryption — minimum 32 characters

Web service (optional)

VariableDefaultPurpose
NUXT_API_BASE_URLhttp://localhost:3000Internal URL from web to API (use Docker service name, e.g., http://api:3000)
NUXT_PUBLIC_API_PUBLIC_URLPublic-facing API URL shown in settings page for OPDS/KoSync endpoints

NODE_ENV=production is baked into both images.

Both services validate environment variables on startup and will fail immediately with a clear error if required variables are missing or invalid.

Volumes

The API container needs persistent, writable storage for two paths:

Mount targetPurpose
Value of NITRO_INBOX_PATHIncoming book files (watched for ingestion)
Value of NITRO_LIBRARY_PATHOrganized book library

Example Docker Compose

yaml
services:
  db:
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_USER: libris
      POSTGRES_PASSWORD: changeme
      POSTGRES_DB: libris
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U libris"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  api:
    image: git.lan.raz.wtf/raz/libris/api:latest
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    environment:
      NITRO_DATABASE_URL: postgresql://libris:changeme@db:5432/libris
      NITRO_REDIS_URL: redis://redis:6379
      NITRO_INBOX_PATH: /data/inbox
      NITRO_LIBRARY_PATH: /data/library
      NITRO_API_SECRET_KEY: # openssl rand -hex 32
    volumes:
      - inbox:/data/inbox
      - library:/data/library
    ports:
      - "3000:3000"

  web:
    image: git.lan.raz.wtf/raz/libris/web:latest
    restart: unless-stopped
    depends_on:
      - api
    environment:
      NUXT_SESSION_PASSWORD: # openssl rand -hex 32
      NUXT_API_BASE_URL: http://api:3000
    ports:
      - "3100:3100"

volumes:
  db_data:
  inbox:
  library:

Database Migrations

Migrations apply automatically on API startup via the 0.migrate.ts server plugin. No manual migration step is needed when deploying a new image.