Docker Compose

The recommended way to run ravyn. Three containers, prebuilt images, no Rust toolchain required on the host.

1. Prerequisites

A Linux server with Docker and the Compose plugin:

docker --version
docker compose version

If either is missing, follow Docker's own install guide for your distro first.

2. Get the compose file

mkdir ravyn && cd ravyn
curl -O https://tryravyn.com/docker-compose.yml

It looks like this:

services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: change-me
      POSTGRES_DB: ravyn
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 2s
      timeout: 5s
      retries: 15

  api:
    image: ghcr.io/officialaudite/ravyn-api:latest
    environment:
      DATABASE_URL: postgres://postgres:change-me@db:5432/ravyn
      STORAGE_ROOT: /data
      THUMBNAIL_ROOT: /data/thumbnails
      LISTEN_ADDR: 0.0.0.0:3000
      # the domain people will actually see, not the container's own address
      RAVYN_PUBLIC_API_URL: https://files.example.com
    ports:
      - "3000:3000"
    volumes:
      - storage:/data
    depends_on:
      db:
        condition: service_healthy

  web:
    image: ghcr.io/officialaudite/ravyn-web:latest
    environment:
      RAVYN_API_URL: http://api:3000
      RAVYN_PUBLIC_API_URL: https://files.example.com
    ports:
      - "3001:3001"
    depends_on:
      - api

volumes:
  db-data:
  storage:
Change both change-me placeholders to a real password before starting this anywhere reachable from the internet.

3. Point it at your domain

Edit RAVYN_PUBLIC_API_URL on both services to the domain you'll actually use. This is what gets embedded in share links and Discord/Slack webhook payloads, and it has to match what a visitor's browser can reach - not the internal api:3000 container address.

4. Start it

docker compose up -d

ravyn-web is on port 3001, ravyn-api on 3000. Put a reverse proxy in front of both for TLS - see the Caddy snippet below.

5. Create the first account

Open the site and register. The very first account on a fresh instance always becomes admin automatically - there's no separate setup step. From /admin that account decides whether anyone else can sign up.

Reverse proxy with Caddy

A minimal Caddyfile for two subdomains, one for the app and one for direct file/API links:

files.example.com {
    reverse_proxy localhost:3001
}

api.example.com {
    reverse_proxy localhost:3000
}

Caddy issues and renews TLS certificates automatically. If you'd rather run everything off one domain, proxy /api/* and file routes to :3000 and everything else to :3001 - ravyn-web already proxies most of what a browser needs, so a single domain works too; see the README's Docker section for the exact route list.

S3-compatible storage

Local disk is the default. To use S3, R2, MinIO, or anything S3-compatible, add these to the api service instead of (or alongside) STORAGE_ROOT:

VariableMeaning
STORAGE_BACKENDs3
S3_BUCKETbucket name
S3_REGIONregion, or auto for R2/most others
S3_ENDPOINTcustom endpoint URL, unset for real AWS S3
S3_ACCESS_KEY_IDaccess key
S3_SECRET_ACCESS_KEYsecret key

Updating

docker compose pull
docker compose up -d

Backing up

Two things need backing up: the db-data volume (a pg_dump is easiest) and either your storage volume (local disk) or your S3 bucket's own backup/versioning, depending which you picked.

docker compose exec db pg_dump -U postgres ravyn > backup.sql
Every feature, configuration option, and endpoint (2FA, quotas, webhooks, chunked uploads, and the rest) is on the features page.