Deploy
Dropkiln runs as one container. By default there is no database. Each artifact is a directory of plain files under /data, so backing up that directory backs up everything. Node ≥ 22, no build step.
docker compose (recommended)
git clone https://github.com/anvilnine/artifacts && cd artifacts
cp .env.example .env # set ARTIFACTS_API_KEY and BASE_URL
docker compose up -d
Generate a key with openssl rand -hex 32. Compose reads .env from the project directory.
docker
docker run -d -p 3000:3000 -v artifacts-data:/data \
-e ARTIFACTS_API_KEY=$(openssl rand -hex 32) \
-e BASE_URL=https://artifacts.example.com \
ghcr.io/anvilnine/artifacts:latest
Configuration
| Env var | Required | Default | Purpose |
|---|---|---|---|
ARTIFACTS_API_KEY |
yes | — | Bootstrap admin bearer, the break-glass key; also mints scoped keys |
BASE_URL |
recommended | http://localhost:3000 |
Public origin in returned URLs; an https:// value marks the session cookie Secure |
ARTIFACTS_ADMIN_USERNAME / ARTIFACTS_ADMIN_PASSWORD |
no | — | Seed the admin account instead of using the first-run setup screen |
STORAGE_BACKEND |
no | local |
local, s3, git, postgres, or sqlite |
PORT |
no | 3000 |
Listen port |
TRUST_PROXY |
no | none |
Client-IP source for rate limiting: none, cloudflare, or xff |
Storage backends
Plain files under /data are perfect when the disk is durable (a mounted volume, a persistent PaaS disk). On hosts that reprovision a fresh container with no volume (Cloud Run, some free tiers), local disk is wiped. Set STORAGE_BACKEND to keep state outside the container:
s3: any S3-compatible bucket (R2, B2, MinIO, Spaces, Wasabi). The bucket must be private; artifacts are always served through the app so their hardening headers apply.git: commit every change to a private remote; a fresh container rehydrates from it, and you get full version history. Run a single writer.postgres: one row per object; handy where you already run Postgres.sqlite: a single file via Node’s built-innode:sqlite. Only as durable as its disk.
Any Dockerfile PaaS
Works on Coolify, CapRover, Dokploy, Railway, and similar: expose port 3000, mount a volume at /data, set the two env vars. A health endpoint lives at GET /healthz.
Serve artifacts from a separate origin
Uploaded HTML executes on the origin it is served from. That’s the product. Serve artifacts (/a/…) from a dedicated origin that serves nothing else, so an uploaded page can never ride the dashboard’s session cookie to call /api/*. See Security.