Database Configuration
Deckyard stores its data in PostgreSQL. PostgreSQL is the only storage backend: the old file-based backend (JSON on disk) was removed in 1.x.
What is stored where
Section titled “What is stored where”- PostgreSQL: presentations, versions, users, settings, libraries, published decks and everything else except media.
- Uploads: uploaded media stays on disk in
server/uploads/(override withUPLOADS_DIR), unless you use an external media provider. - Data directory:
server/data/(override withDATA_DIR) now only holds the deck-thumbnail cache, plus whatever an older install left behind.
Docker Compose
Section titled “Docker Compose”The compose stack ships its own postgres service (PostgreSQL 16, data in the pg_data volume) and the app container uses it out of the box. Nothing needs to be set, and migrations are applied automatically at container start.
To use a managed database instead, set the connection variables below in .env. Do not set DATABASE_HOST=localhost for compose: inside the container localhost is the app itself, not the database (the bundled one is the host postgres). The container entrypoint refuses to start on a localhost value.
Running without Docker
Section titled “Running without Docker”When you run npm start from a checkout, point the connection variables at your own PostgreSQL and apply the migrations once (and again after each update):
npm run db:migrateHere DATABASE_HOST=localhost is the normal value.
Connection
Section titled “Connection”Using individual variables:
DATABASE_HOST=localhostDATABASE_PORT=5432DATABASE_NAME=deckyardDATABASE_USER=deckyardDATABASE_PASSWORD=your-secure-passwordUsing a connection URL:
DATABASE_URL=postgres://deckyard:password@localhost:5432/deckyardWhen DATABASE_URL is set it is the complete override: host, port, database, user and password all come from the URL, never a mix with the DATABASE_* variables. The SSL and pool settings below still apply on top. The app, db:migrate and db:import all read the same settings.
STORAGE_MODE defaults to postgres and can be left unset. postgres is its only accepted value; file, postgresql or anything else stops the server at boot.
SSL is enabled by default for any host other than localhost or 127.0.0.1.
# Disable SSL (e.g. an internal network)DATABASE_SSL=false
# Allow self-signed certificates (e.g. managed database services)DATABASE_SSL_REJECT_UNAUTHORIZED=falseThe compose stack sets DATABASE_SSL=false by default, since the bundled database runs on the internal Docker network.
Connection Pool
Section titled “Connection Pool”DATABASE_POOL_MIN=2 # Minimum connections (default: 2)DATABASE_POOL_MAX=10 # Maximum connections (default: 10)Migrations
Section titled “Migrations”Schema migrations live in server/db/migrations/ and are tracked in a _migrations table, so running them again is a no-op.
- Docker: the container entrypoint runs pending migrations before the server starts, retrying while the database comes up.
- Without Docker: the server does not migrate on its own; run
npm run db:migrateafter installing or updating.
npm run db:migrate # Apply pending migrationsnpm run db:migrate:status # Show applied and pending migrationsnpm run db:migrate:down # Roll back the most recent migrationIn a running compose stack, prefix these with docker compose exec app.
Upgrading from file-based storage
Section titled “Upgrading from file-based storage”An install that used file storage still has its decks under server/data/. Deckyard refuses to start when it finds decks on disk while the database holds none, so you get a stopped server instead of an empty organization. Import the data once:
npm run db:migrate # not needed on Docker; the entrypoint already did thisnpm run db:import # add -- --dry-run to preview firstIn compose: docker compose exec app npm run db:import. The import is idempotent and never modifies your files. It covers presentations, tags, the image and slide libraries, slide collections and published presentations; version history is moved by a schema migration. Remove STORAGE_MODE=file from your environment if it is still set.
Once PostgreSQL is verified to hold your data, the leftover files can be cleaned up with scripts/prune-legacy-data.js. Do not delete them by hand: the boot check uses server/data/presentations/ to detect an un-imported install.
Backups
Section titled “Backups”Back up the database together with server/uploads/. On the compose stack:
docker compose exec postgres sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > backup.sqlOutside compose, back up your own PostgreSQL the same way, plus server/uploads/.
Environment Variables Reference
Section titled “Environment Variables Reference”| Variable | Default | Description |
|---|---|---|
STORAGE_MODE | postgres | Storage backend; postgres is the only accepted value |
DATABASE_URL | (unset) | Full connection URL; overrides the host, port, name, user and password variables |
DATABASE_HOST | localhost (postgres in compose) | PostgreSQL host |
DATABASE_PORT | 5432 | PostgreSQL port |
DATABASE_NAME | deckyard | Database name |
DATABASE_USER | deckyard | Database user |
DATABASE_PASSWORD | (empty; deckyard in compose) | Database password |
DATABASE_SSL | true for non-localhost hosts (false in compose) | Enable SSL |
DATABASE_SSL_REJECT_UNAUTHORIZED | true | Reject self-signed certs |
DATABASE_POOL_MIN | 2 | Minimum pool connections |
DATABASE_POOL_MAX | 10 | Maximum pool connections |
DATA_DIR | server/data | Data directory (thumbnail cache, legacy files) |
UPLOADS_DIR | server/uploads | Uploaded media directory |