omnipaper

Installation

Self-host omnipaper with Docker Compose.

Create the Compose file

Generate two secrets — run this twice, once for each:

openssl rand -base64 32

Paste the values into docker-compose.yml:

docker-compose.yml
services:
  postgres:
    image: postgres:18
    restart: unless-stopped
    environment:
      POSTGRES_USER: omnipaper
      POSTGRES_PASSWORD: pg-password-change-me
      POSTGRES_DB: omnipaper
    volumes:
      - postgres-data:/var/lib/postgresql/data

  app:
    image: ghcr.io/omnipaper/omnipaper:0
    restart: unless-stopped
    depends_on:
      - postgres
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://omnipaper:pg-password-change-me@postgres:5432/omnipaper
      # Paste a separate `openssl rand -base64 32` value into each
      AUTH_SECRET: replace-me
      ENCRYPTION_KEY: replace-me

volumes:
  postgres-data:

Image tags

  • 0 — the latest pre-1.0 release (tracks the 0.x line).
  • 0.x — the latest patch within the minor line.
  • 0.x.y — a specific, pinned version.

Pin a specific 0.x.y version (as in the file above). The rolling 0 and 0.x tags move as new builds ship and may be unstable before 1.0.

Start it

docker compose up -d

Open http://localhost:3000 and create your first account. See First run for what happens next.

Storage is not configured at deploy time. After your first login, connect an S3-compatible bucket (AWS S3, Cloudflare R2, MinIO, …) from the app's settings. See Storage.

Environment variables

VariableRequiredDefaultDescription
DATABASE_URLPostgreSQL connection string.
AUTH_SECRETSigns auth sessions and cookies. Use a random 32-byte value.
ENCRYPTION_KEYSee Environment variables.
APP_URLderived from requestPublic URL of your instance. See Custom domain.
PORT3000Port the app listens on inside the container.

See Environment variables for the full reference.

Custom domain

omnipaper runs behind a reverse proxy (Caddy, Traefik, nginx, …) with no extra configuration: when APP_URL is unset it derives its origin from the incoming request. Just make sure your proxy forwards X-Forwarded-Host and X-Forwarded-Proto.

To pin the origin to a fixed value, add APP_URL to the app service's environment

docker-compose.yml
services:
  app:
    environment:
      DATABASE_URL: postgres://omnipaper:change-me@postgres:5432/omnipaper
      AUTH_SECRET: replace-me
      ENCRYPTION_KEY: replace-me
      APP_URL: https://omnipaper.example.com   # add this line

Include the scheme (https://) and no trailing slash.

Terminate TLS at your reverse proxy and forward to the app's port 3000. omnipaper serves plain HTTP inside the container.

Updating

docker compose pull
docker compose up -d

Migrations run automatically on start, so pull-and-up is all that's needed.

On this page