LogoDicto Wiki

Set up the server

A simple container deployment with PostgreSQL, RustFS and the management app.

The stack#

Run one container for each responsibility:

ContainerPurpose
postgresDatabase with the extensions Dicto requires
db-migratorOne-shot database migration before the server starts
serverDicto API
rustfsRecording and file storage
managementBrowser-based administration

The migration container uses the same release image as the server, with migrate as its command. It exits after successful migration; the server waits for that successful exit before starting. Prometheus can be added separately.

1. Download the files#

Save these files together in your deployment directory:

The PostgreSQL build file installs pgvector and pg_textsearch, which are required by the database migrations. It builds PostgreSQL 18 with a persistent named volume.

The example uses the default GHCR images with :latest. Authenticate to GHCR if those images are private.

2. Configure your installation#

In secrets.env, set the public API URL and replace every credential placeholder. Generate a URL-safe database password, for example with openssl rand -hex 24; Compose uses it for both PostgreSQL and the server connection string. Keep the auth secret stable across restarts.

chmod 600 secrets.env

In server.yaml, change the public auth URL, trusted browser origins, transcription endpoint and model name for your organization. Add an end-user web app's origin and callback if you also host that client. The management app's API_URL must be reachable from users' browsers.

Recording storage points to http://rustfs:9000 inside the container network. allow_http: true permits that internal connection; expose the public API and management app through your HTTPS reverse proxy.

Server YAML references are complete string values such as `"${AUTH_SECRET}"`. Provider API roots must include any required `/v1` or gateway prefix. Do not put server credentials into the management app's browser configuration.

3. Define the containers#

The downloaded compose.yaml contains this configuration:

services:
  postgres:
    build:
      context: .
      dockerfile: postgres.Dockerfile
    restart: unless-stopped
    environment:
      POSTGRES_DB: dicto
      POSTGRES_USER: dicto
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres-data:/var/lib/postgresql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U dicto -d dicto"]
      interval: 5s
      timeout: 5s
      retries: 20

  rustfs:
    image: rustfs/rustfs:latest
    restart: unless-stopped
    environment:
      RUSTFS_ACCESS_KEY: ${STORAGE_ACCESS_KEY_ID}
      RUSTFS_SECRET_KEY: ${STORAGE_SECRET_ACCESS_KEY}
      RUSTFS_CONSOLE_ENABLE: "true"
    volumes:
      - rustfs-data:/data
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/health"]
      interval: 5s
      timeout: 5s
      retries: 20

  db-migrator:
    image: ghcr.io/truhnlab/dicto-server:latest
    command: ["migrate"]
    restart: "no"
    env_file: secrets.env
    environment:
      DICTO_CONFIG_FILE: /etc/dicto/server.yaml
      DB_URL: postgresql://dicto:${DB_PASSWORD}@postgres:5432/dicto
    volumes:
      - ./server.yaml:/etc/dicto/server.yaml:ro
    depends_on:
      postgres:
        condition: service_healthy

  server:
    image: ghcr.io/truhnlab/dicto-server:latest
    command: ["serve"]
    restart: unless-stopped
    env_file: secrets.env
    environment:
      DICTO_CONFIG_FILE: /etc/dicto/server.yaml
      DB_URL: postgresql://dicto:${DB_PASSWORD}@postgres:5432/dicto
    volumes:
      - ./server.yaml:/etc/dicto/server.yaml:ro
    depends_on:
      db-migrator:
        condition: service_completed_successfully
      rustfs:
        condition: service_healthy

  management:
    image: ghcr.io/truhnlab/dicto-management-app:latest
    restart: unless-stopped
    environment:
      API_URL: ${API_URL}
      BASE_HREF: /
    depends_on:
      server:
        condition: service_started

volumes:
  postgres-data:
  rustfs-data:

Database and object storage data live in named volumes. The containers communicate over the stack's Docker network. Attach your reverse proxy to that same network and route the API and management hostnames to the corresponding container and listening port.

Listening ports#

ServiceContainer addressPurpose
Dicto server server:3000 Public API through your HTTPS reverse proxy
Management app management:80 Administration interface through your HTTPS reverse proxy
PostgreSQLpostgres:5432Internal database connection
RustFS S3 APIrustfs:9000Internal recording and file storage
RustFS consolerustfs:9001Storage administration
Database migratorNoneRuns migrations and exits
Dicto monitoring, when enabled server:9091 Internal metrics endpoint
Prometheus, when addedprometheus:9090Monitoring interface

The server listens on 0.0.0.0:3000, as configured in server.yaml. These are container listening ports; the example uses the shared Docker network for access rather than publishing ports on the host.

4. Start database and storage#

docker compose --env-file secrets.env up --build -d postgres rustfs

Route an administrator-only hostname through your reverse proxy to rustfs:9001 and open the RustFS console there. Sign in with the configured storage access key and secret, and create the private recordings bucket. The named volume persists its contents. See the RustFS container guide for console and storage administration.

5. Validate and start Dicto#

docker compose --env-file secrets.env run --rm --no-deps db-migrator validate-config
docker compose --env-file secrets.env run --rm --no-deps db-migrator validate-config --resolve-env
docker compose --env-file secrets.env up --build -d

The final command runs the migration container and starts the server only if migrations succeed. Validation checks configuration structure and values; it does not test inference credentials or storage permissions.

The example temporarily enables admin seeding for an empty database. Set ADMIN_EMAIL and ADMIN_PASSWORD before first startup. After signing in successfully, replace the complete seed section with the following, remove the bootstrap credentials from secrets.env, and recreate the server:

seed:
  admin:
    enabled: false
docker compose --env-file secrets.env up -d --force-recreate server

The seeder skips creation when users already exist. It is not a password-reset mechanism.

6. Verify the installation#

docker compose --env-file secrets.env ps --all
docker compose --env-file secrets.env logs --tail=100 db-migrator server
curl --fail https://api.dicto.example.com/

The migration container should show a successful exit; the other containers should be running. Route your management hostname to management:80, sign in, and follow client setup for a test recording and transcription.

Optional: add Prometheus#

Download prometheus.compose.yaml and prometheus.yaml into the same directory.

In server.yaml, replace the monitoring section with:

monitoring:
  enabled: true
  host: 0.0.0.0
  port: 9091

The monitoring endpoint stays on the internal container network. The optional Compose file adds Prometheus:

services:
  prometheus:
    image: prom/prometheus:latest
    restart: unless-stopped
    volumes:
      - ./prometheus.yaml:/etc/prometheus/prometheus.yml:ro
      - prometheus-data:/prometheus
    depends_on:
      server:
        condition: service_started

volumes:
  prometheus-data:

Its scrape configuration targets Dicto's monitoring port:

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: dicto
    metrics_path: /metrics
    static_configs:
      - targets: ["server:9091"]

Start the additional service and reload the server configuration:

docker compose --env-file secrets.env -f compose.yaml -f prometheus.compose.yaml up -d --force-recreate server prometheus

Route an administrator-only hostname through your reverse proxy to prometheus:9090 and open the monitoring interface there. Check the dicto target is UP. The Prometheus configuration reference describes scrape settings.

To make Prometheus available as a source for management dashboards, add this server configuration:

management:
  sources:
    monitoring:
      type: prometheus
      url: http://prometheus:9090

The management UI queries this source through the Dicto server. Add dashboard queries using the monitoring source when you want to display these metrics; the built-in database dashboards remain available.

Updates and backups#

Back up PostgreSQL and recording storage together and test restoration. To update the :latest images, pull them and rerun the migration container before replacing the server:

docker compose --env-file secrets.env stop server management
docker compose --env-file secrets.env pull server db-migrator management
docker compose --env-file secrets.env run --rm db-migrator migrate
docker compose --env-file secrets.env up -d server management

Use both Compose files when managing an installation with Prometheus. An older server image does not reverse database migrations.