Self-hosting › Installation
Installation
spellcache runs as four containers: web (the Next.js app), worker (Scryfall
import, nightly prices and valuations), postgres and redis. Images are
published on GHCR for linux/amd64 and linux/arm64 (Raspberry Pi 4/5, ARM NAS).
HTTPS and the reverse proxy are up to you: web listens on a local port and
any proxy can sit in front of it (nginx, Traefik, Caddy, Cloudflare Tunnel…).
Requirements
| Item | Details |
|---|---|
| Server | 4 GB RAM, 2 vCPU, ~40 GB disk. The memory limits of the four services add up to 2.8 GB. |
| OS | Any Linux with Docker; tested on Debian 12 / Ubuntu 24.04 |
| Docker | Docker Engine ≥ 24 with the docker compose v2 plugin |
| Reverse proxy | Any proxy that terminates HTTPS for your domain (see Reverse proxy) |
| Host tools | postgresql-client-17 and openssl, for backup and restore |
| A Resend account with a verified sending domain (see Email) |
sudo apt update && sudo apt install -y postgresql-client-17 openssl curlQuick install
The installer downloads the files of the latest release into /srv/spellcache
(no clone, no build), asks for the public URL, the administrator’s email and
the Resend key, generates the secrets, starts the containers and queues the
catalogue import:
curl -fsSL https://get.spellcache.com | sudo shRead it first if you prefer:
install.sh.
To install elsewhere or pick a version, pass the variables after sudo
(… | sudo SPELLCACHE_DIR=/opt/spellcache SPELLCACHE_VERSION=1.2.3 sh). Run the
same command again later to update.
It covers the steps from Get the files to Import the card catalogue.
Then put your reverse proxy in front of 127.0.0.1:3000,
create the first account and set up the
scheduled tasks. The rest of this guide applies as is:
the files keep their place in the repository.
The sections below do the same by hand.
Get the files
Download them from the latest release, keeping their paths:
sudo mkdir -p /srv/spellcache/deploy && cd /srv/spellcacheTAG=$(curl -fsSL https://api.github.com/repos/spellcache/spellcache/releases/latest | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p')for f in docker-compose.example.yml .env.example deploy/backup.sh deploy/restore.sh; do sudo curl -fsSL "https://raw.githubusercontent.com/spellcache/spellcache/$TAG/$f" -o "$f"donesudo chmod +x deploy/*.shOr clone the repository
(git clone https://github.com/spellcache/spellcache.git /srv/spellcache).
Configure
cp .env.example .envchmod 600 .env${EDITOR:-nano} .envEvery variable is documented in the file and in the configuration reference; the Production section lists what self-hosting needs. Three values are random secrets:
openssl rand -base64 32 # AUTH_SECRETopenssl rand -hex 24 # REDIS_PASSWORDopenssl rand -base64 32 # BACKUP_PASSPHRASEKeep a copy of BACKUP_PASSPHRASE somewhere else than the server. An archive
cannot be restored without it.
AUTH_URL is the full public URL (e.g. https://spellcache.example.com).
DATABASE_URL points to 127.0.0.1:5432: that is the host’s view of the
database, used by the backup and restore scripts. Containers reach
postgres:5432, and the Compose file builds their URL by itself.
The file must be named .env: it is the one docker compose reads for
${...} interpolation. It is ignored by git and never copied into an image.
Start
docker compose -f docker-compose.example.yml up -dThen point your reverse proxy to 127.0.0.1:3000 (see
Reverse proxy), and check that every service becomes
healthy (within about two minutes):
docker compose -f docker-compose.example.yml psdocker compose -f docker-compose.example.yml logs -f webStart-up order:
postgresandredisstart;webapplies the database migrations, then starts the server. It staysstartingin between and serves no request;workerwaits forwebto behealthy(it never migrates by itself).
To pin a version instead of latest, set SPELLCACHE_VERSION=1.2.3 (or 1.2)
in .env.
Import the card catalogue
The app is empty until the Scryfall catalogue is imported. Trigger the first import by hand (twenty minutes to an hour):
docker compose -f docker-compose.example.yml exec -T worker node apps/worker/src/index.ts --run-nowdocker compose -f docker-compose.example.yml logs -f workerAfter that, the worker imports and revalues every night at WORKER_NIGHTLY_AT
in WORKER_TIMEZONE. An administrator can also trigger both jobs from
Settings › Administration › Jobs.
First account
The first account created on an empty database becomes the administrator, and
only ADMIN_EMAIL may create it. Open the app, sign in with that address, then
invite other users from Settings › Administration › Users (sign-ups are
invite-only by default). See Administration.
Next steps
- Scheduled tasks: one cron entry for the nightly run, one for the backup. Set them up on day one.
- Backup: run a first backup by hand and check that the archive lands where you expect.