Skip to content

PostgreSQL

Scryer uses SQLite by default. To use PostgreSQL, set SCRYER_DB_URL before starting Scryer. The URL must include a host, database name, and an explicit sslmode. Scryer also requires a username and password, which can be supplied separately from the URL.

Set these variables in your container environment or in the environment of the service that starts Scryer. For Homebrew, use the service configuration file described in Homebrew.

SCRYER_DB_URL=postgres://db:5432/scryer?sslmode=disable
SCRYER_DB_USER=scryer
SCRYER_DB_PASSWORD=replace-with-a-strong-password

This example assumes a PostgreSQL service named db on a trusted, same-host Docker network. For a remote database, use its reachable hostname and configure TLS.

SettingBehavior
SCRYER_DB_URLAccepts postgres:// or postgresql://. Takes precedence over SCRYER_DB_PATH.
SCRYER_DB_USEROverrides a username embedded in the URL.
SCRYER_DB_PASSWORDUsed when no password file is configured; overrides a URL password.
SCRYER_DB_PASSWORD_FILEOptional alternative: reads the password from a file, such as /run/secrets/scryer_db_password. Takes precedence over both the password environment variable and a URL password.

Optionally, set SCRYER_DB_PASSWORD_FILE instead of SCRYER_DB_PASSWORD to read the password from a mounted file or Docker secret. The path must exist and be readable inside the Scryer container. An unreadable or empty file stops startup instead of falling back to another password source. Scryer trims trailing whitespace from file contents; it does not trim SCRYER_DB_PASSWORD. See Configuration for the full settings reference.

This example uses a dedicated PostgreSQL 18 container. Save the following as docker-compose.yml, replacing /path/to/media with your media directory and both replace-with-a-strong-password values with the same strong password. Keep files containing real passwords out of version control.

services:
db:
image: postgres:18
restart: unless-stopped
environment:
POSTGRES_USER: scryer
POSTGRES_DB: scryer
POSTGRES_PASSWORD: "replace-with-a-strong-password"
volumes:
- postgres-data:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U scryer -d scryer"]
interval: 5s
timeout: 5s
retries: 12
start_period: 10s
scryer:
image: ghcr.io/scryer-media/scryer:latest
restart: unless-stopped
depends_on:
db:
condition: service_healthy
ports:
- "8080:8080"
environment:
SCRYER_DB_URL: "postgres://db:5432/scryer?sslmode=disable"
SCRYER_DB_USER: scryer
SCRYER_DB_PASSWORD: "replace-with-a-strong-password"
volumes:
- scryer-config:/config
- /path/to/media:/data
volumes:
postgres-data:
scryer-config:

PostgreSQL is reachable as db:5432 by Scryer without publishing a database port on the host. localhost inside Scryer’s container would refer to Scryer’s own container. The disable TLS mode is limited to this trusted local network example.

The official image creates POSTGRES_USER as a database superuser, so this compact example is for a database server dedicated to Scryer. For a shared or managed server, use the separate application role in Using an Existing PostgreSQL Server. Image initialization variables only apply to an empty data directory; changing POSTGRES_PASSWORD later does not rotate an existing database password. PostgreSQL 18 uses the /var/lib/postgresql volume mount shown above; 17 and older use /var/lib/postgresql/data. See the official PostgreSQL image documentation.

Start and inspect the stack:

Terminal window
docker compose up -d
docker compose ps
docker compose logs --tail=100 db scryer

In Scryer’s logs, find datastore configuration resolved and confirm its engine is postgres and its config_source is SCRYER_DB_URL. This confirms which datastore Scryer selected; it is logged before the connection is opened, so also check for connection or migration errors and wait for startup to finish.

Once Scryer is ready, open http://localhost:8080 and complete setup. Scryer applies its database schema migrations during normal startup. A successful PostgreSQL health check alone does not prove Scryer authenticated successfully.

Keep both named volumes. PostgreSQL holds the application database, while /config remains necessary for Scryer’s persistent local state, including file-based encryption key storage and backups. Keep your encryption key recoverable alongside your database backups. Media and completed downloads still need the shared mount described in Library Layout.

Have a database administrator create a dedicated login role and database. For example, in an administrative psql session:

CREATE ROLE scryer LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE;
\password scryer
CREATE DATABASE scryer OWNER scryer;

The \password command prompts for a password without placing it in SQL history. Set SCRYER_DB_PASSWORD to that same password. Scryer needs permission to create and alter its schema during startup migrations, so a read/write-only role is insufficient. On a server with customized schema permissions, ensure the role can use and create objects in the target database’s public schema.

Set the connection URL to this database, configure TLS below, and allow connections from the Scryer host in the server’s network and PostgreSQL access rules. For managed PostgreSQL, follow the provider’s database ownership, connection, and certificate requirements. Scryer initializes tables in the database; it does not provision the database server, login role, or database itself.

For a server reached over a LAN or other remote network, use certificate validation with a hostname matching the server certificate:

SCRYER_DB_URL=postgres://db.example.com:5432/scryer?sslmode=verify-full&sslrootcert=/run/secrets/postgres_ca.crt
SCRYER_DB_USER=scryer
SCRYER_DB_PASSWORD=replace-with-a-strong-password

Mount the database provider’s trusted CA certificate at the sslrootcert path inside Scryer’s container, or supply a readable absolute path for a native service. The certificate path and hostname must be valid from Scryer’s environment.

Scryer accepts these explicit modes:

sslmodeConnection behavior
disableNo TLS; use only for a trusted local connection.
preferAttempts TLS but permits an unencrypted fallback.
requireRequires TLS without validating server identity.
verify-caRequires TLS and validates the certificate authority.
verify-fullRequires TLS and validates both the certificate authority and hostname.

These are the TLS modes used by Scryer’s PostgreSQL driver. Use verify-full when server identity must be verified. Scryer rejects a URL with no sslmode, even though database clients may otherwise have their own defaults.

SymptomWhat to check
URL validation failsInclude the host, /database-name, and an accepted sslmode. Supply a username and password.
Password file cannot be readCheck the secret mount and permissions for Scryer’s runtime user. A host path alone does not make the file available inside the container.
Password authentication failsConfirm the role’s actual password matches SCRYER_DB_PASSWORD. An existing PostgreSQL volume ignores initialization password changes. Check for a higher-precedence password file or trailing whitespace in an environment password.
Connection refused or hostname not foundUse the Compose service name for containers on the same network. For an external server, check its listener, DNS, port, firewall, and access rules.
TLS or certificate verification failsConfirm the server supports TLS, the CA file is readable inside Scryer, and the URL hostname matches the certificate.
Permission denied during migrationsEnsure the Scryer role owns its database objects and has schema creation privileges.
Too many connectionsReview the PostgreSQL connection budget and Scryer’s pool limit alongside other clients.
Setup appears instead of the existing libraryCheck which database the URL selects. Switching from SQLite does not migrate its contents.
  • Configuration — full startup environment reference
  • Docker — container setup and persistent media mounts
  • Networking — Scryer’s web listener, reverse proxy, and container connectivity
  • Upgrading — application upgrade procedures