ANVILNINE
On this page

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.

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-in node: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.

Last updated