Skip to content

Deployment ​

Deploy Vdoc with one docker-compose.yml. Settings, accounts, passwords, and internal keys all live in this file. No .env, initialization scripts, or source checkout is required.

Install Docker Engine / Docker Desktop and Docker Compose v2. Published Linux images support amd64 and arm64. The example below runs locally; existing installations should read Upgrade and Rollback first.

1. Download the Compose File ​

Download docker-compose.yml into a dedicated deployment directory, or run:

sh
mkdir vdoc-deploy
cd vdoc-deploy
curl -fLO https://chnmig.github.io/Vdoc-site/downloads/docker-compose.yml
chmod 600 docker-compose.yml

The website serves the current stable release. For a specific version, download the same YAML from the v0.3.0 Release. Its docker-compose.yml.sha256 attachment can verify the downloaded bytes.

2. Fill In Settings and Your Login ​

Edit docker-compose.yml, replace every CHANGE_ME value, and set the administrator email and name. Quote passwords in YAML. Write a literal $ as $$ so Compose does not interpret it as an environment variable.

SettingPurpose
VDOC_DATABASE_PASSWORDPostgreSQL password, shared with its container through a YAML anchor
VDOC_STORAGE_ACCESS_KEY / VDOC_STORAGE_SECRET_KEYRustFS account and password, also shared through anchors
VDOC_JWT_KEYSigns and verifies login credentials; use an independent random key of at least 32 characters
VDOC_MCP_TOKEN_CIPHER_KEYEncrypts stored MCP tokens, AI Provider keys, and share capabilities; use another random key of at least 32 characters
VDOC_INITIAL_ADMIN_EMAIL / VDOC_INITIAL_ADMIN_NAMEInitial administrator email and name
VDOC_INITIAL_ADMIN_PASSWORDInitial administrator password, 12–72 bytes

RustFS requires an access key of at least 3 characters and a secret key of at least 8 characters. Generate values with a password manager, or run openssl rand -hex 32 separately for the JWT and MCP keys.

You supply these values and keep them in Compose; the backend does not create a separate key file. Preserve them during upgrades. Changing the JWT key invalidates existing login credentials; losing the MCP encryption key prevents reading affected stored ciphertext. Keep your completed YAML private.

The initial administrator is created only when the user table is empty. Restarting or upgrading does not create duplicates or overwrite passwords changed later by users. Public registration is disabled by default; the backend refuses to start with an empty database and no usable administrator configuration.

3. Start Vdoc ​

sh
docker compose pull
docker compose up -d
docker compose ps

Compose first runs config-check once. If placeholders remain or required settings are invalid, validation fails before PostgreSQL or RustFS initializes. Correct the YAML and run docker compose up -d again.

After validation, Compose starts PostgreSQL, RustFS, Backend, and Admin. Backend creates tables, applies pending database migrations, creates the storage bucket, and initializes the first administrator during startup. An Exited (0) status for the completed config-check container is expected.

Open the Vdoc workbench and sign in with your configured administrator account. Backend health is available at http://127.0.0.1:8080/api/v1/open/health.

Next: publish your first document and query it with an agent.

Complete Compose Example ​

This example includes all settings, four persistent services, a one-shot configuration check, health checks, and data volumes. It reads directly from the published YAML source and matches the download. Replace the placeholders before starting.

yaml
# Save this file as docker-compose.yml. Replace every CHANGE_ME value below.
# Keep this private file, its credentials, and the project name when upgrading.
# Use Docker Compose v2: docker compose pull && docker compose up -d
name: vdoc

x-backend-image: &backend-image ghcr.io/chnmig/vdoc:v0.3.0
x-admin-image: &admin-image ghcr.io/chnmig/vdoc-admin:v0.3.0

# All deployment settings are here; no .env or initialization script is needed.
# A literal dollar sign in any value must be written as $$ for Docker Compose.
x-backend-environment: &backend-environment
  model: release
  VDOC_SERVER_PORT: "8080"
  VDOC_SERVER_PID_FILE: ""
  VDOC_SERVER_CORS_ALLOWED_ORIGINS: "http://127.0.0.1:8081,http://localhost:8081"
  VDOC_DATABASE_ENABLED: "true"
  VDOC_DATABASE_HOST: postgres
  VDOC_DATABASE_PORT: "5432"
  VDOC_DATABASE_NAME: &database-name vdoc
  VDOC_DATABASE_USER: &database-user vdoc
  VDOC_DATABASE_PASSWORD: &database-password "CHANGE_ME_DATABASE_PASSWORD"
  VDOC_DATABASE_SSL_MODE: disable
  VDOC_STORAGE_ENABLED: "true"
  VDOC_STORAGE_ENDPOINT: rustfs:9000
  VDOC_STORAGE_BUCKET: vdoc
  VDOC_STORAGE_ACCESS_KEY: &storage-user "CHANGE_ME_STORAGE_ACCESS_KEY"
  VDOC_STORAGE_SECRET_KEY: &storage-password "CHANGE_ME_STORAGE_SECRET_KEY"
  VDOC_STORAGE_USE_SSL: "false"
  VDOC_STORAGE_PATH_STYLE: "true"
  # Use independent random keys of at least 32 characters. Preserve on upgrade.
  VDOC_JWT_KEY: "CHANGE_ME_JWT_KEY"
  VDOC_MCP_TOKEN_CIPHER_KEY: "CHANGE_ME_MCP_ENCRYPTION_KEY"
  VDOC_MCP_TOKEN_CIPHER_KID: local-aes-gcm-v1
  VDOC_MCP_TOKEN_CIPHER_KEYRING: "{}"
  VDOC_AUTH_ALLOW_REGISTRATION: "false"
  VDOC_INITIAL_ADMIN_EMAIL: "admin@example.com"
  VDOC_INITIAL_ADMIN_NAME: "Vdoc Admin"
  VDOC_INITIAL_ADMIN_PASSWORD: "CHANGE_ME_INITIAL_ADMIN_PASSWORD"

services:
  # Runs the backend's read-only validator before PostgreSQL initializes data.
  config-check:
    image: *backend-image
    command: ["--check-config"]
    environment: *backend-environment
    restart: "no"
    healthcheck:
      disable: true

  postgres:
    image: postgres:18@sha256:06cad38a5d9f5d24b4d83d86def30795d5e4b757fedbf5281172b576dedcd941
    restart: unless-stopped
    depends_on:
      config-check:
        condition: service_completed_successfully
    environment:
      POSTGRES_DB: *database-name
      POSTGRES_USER: *database-user
      POSTGRES_PASSWORD: *database-password
      TZ: Asia/Shanghai
    volumes:
      - postgres-data:/var/lib/postgresql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
      interval: 10s
      timeout: 5s
      retries: 10

  rustfs:
    image: rustfs/rustfs:1.0.0-beta.10@sha256:60f4f2f41ce95216f8cac676e69f9d90c0bfec458a3bc7fd7fb9b7c2452ac57a
    restart: unless-stopped
    depends_on:
      config-check:
        condition: service_completed_successfully
    environment:
      RUSTFS_VOLUMES: /data
      RUSTFS_ADDRESS: 0.0.0.0:9000
      RUSTFS_CONSOLE_ADDRESS: 0.0.0.0:9001
      RUSTFS_CONSOLE_ENABLE: "true"
      RUSTFS_ACCESS_KEY: *storage-user
      RUSTFS_SECRET_KEY: *storage-password
    volumes:
      - rustfs-data:/data
      - rustfs-logs:/app/logs
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://127.0.0.1:9000/health"]
      interval: 10s
      timeout: 5s
      retries: 12
      start_period: 40s

  backend:
    image: *backend-image
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
      rustfs:
        condition: service_healthy
    environment: *backend-environment
    ports:
      - "127.0.0.1:8080:8080"

  admin:
    image: *admin-image
    restart: unless-stopped
    depends_on:
      backend:
        condition: service_healthy
    environment:
      VDOC_ADMIN_API_BASE_URL: "http://127.0.0.1:8080"
    ports:
      - "127.0.0.1:8081:8080"
    healthcheck:
      test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:8080/ || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 6

volumes:
  postgres-data:
  rustfs-data:
  rustfs-logs:

Update the Version ​

Back up your data, update the x-backend-image and x-admin-image version tags in your existing YAML, and preserve all other settings. Then run:

sh
docker compose pull
docker compose up -d

Backend applies migrations included in the new version; completed migrations are not repeated. This does not track the latest release automatically: the deployer chooses when to update. A failed migration prevents normal backend startup. Follow Upgrade and Rollback to inspect failures and recover.

Daily Management and Persistence ​

Run these commands from the directory containing your YAML:

sh
docker compose ps
docker compose logs --tail=100 backend admin postgres rustfs
docker compose stop
docker compose up -d

docker compose down removes containers and networks while preserving named volumes. Do not use docker compose down -v for data you need to keep: it deletes postgres-data, rustfs-data, and rustfs-logs. Keep the Compose project name vdoc; changing it selects different volumes.

PostgreSQL 18 mounts its data volume at /var/lib/postgresql. Vdoc application migrations do not upgrade the PostgreSQL major version. Older database majors require a separate pg_upgrade or dump/restore procedure.

Deploy on a Server ​

Ports bind to 127.0.0.1 by default, suitable for local access or an HTTPS reverse proxy on the same server. PostgreSQL and RustFS are available only inside the Compose network.

For server domains, set VDOC_ADMIN_API_BASE_URL to the browser-accessible HTTPS backend URL and VDOC_SERVER_CORS_ALLOWED_ORIGINS to the exact HTTPS workbench origin in YAML. If your proxy runs in another container or host, also adjust ports or networking so it can reach Backend and Admin.

Backend uses postgres:5432 and rustfs:9000 internally. Do not use http://backend:8080 as the browser API URL: browsers cannot resolve Compose service names. Apply configuration edits with docker compose up -d.

Source Development and Advanced Settings ​

Application deployments do not require the source bootstrap archive. To modify code, run disposable E2E tests, or use external database/object storage services, see the public workspace deployment guide. The source workspace retains .env, test database scripts, and exact source locks for development and release verification.

Key rotation must account for stored ciphertext; replacing a key alone is insufficient. Follow the operations guide to configure historical KIDs and the keyring, and verify the rewrite before removing old keys.

Your team reviews and publishes. Agents query through MCP.