Skip to content

For operators

Artist Alley aims to be simple to operate. One binary, one database, one volume. There’s no message bus and no microservices in the core — the heavier add-ons (Blender worker, AI models, the CLIP visual-search encoder) run as optional companion containers when you turn them on.

  • Single binary. The Go binary embeds the SvelteKit SPA via go:embed. Updating the app is a binary (or image) swap.
  • Postgres-only. No MySQL, no Redis. See ADR 0005.
  • Configuration via env. Everything is set through environment variables, with sensible defaults for development.
  • Backup is pg_dump + the filestore. Two things to back up. No proprietary export format.

Ships as a Docker image (multi-arch, GHCR + Docker Hub). Native packages and a systemd path are on the roadmap for v1.0.0 (#242). Follow the Install guide.

For a quick try via Docker — AA_MASTER_KEY (at-rest crypto; the server won’t boot without it) and AA_SCRAMBLE_KEY (password-hashing pepper) must be set and kept stable across restarts:

Terminal window
docker run -d --name artist-alley \
-p 8080:8080 \
-e AA_DB_HOST=postgres \
-e AA_DB_PASSWORD=secret \
-e AA_MASTER_KEY=$(openssl rand -base64 32) \
-e AA_SCRAMBLE_KEY=$(openssl rand -hex 32) \
-v aa-storage:/var/lib/aa-storage \
ghcr.io/artist-alley-org/artist-alley:latest
ChannelUpdated whenUse for
:vX.Y.Zexact tagproduction (pin this)
:vX.Y, :vXlatest patch on that minor/majortolerant production
:latestmost recent stable release”always shiny”
:edgelatest dev commitpreview / pre-merge testing
:edge-{sha}exact dev commitpinning a specific dev build

Docker images are signed via Sigstore — see the Install guide § Verifying signatures.

Any Postgres 14+ works. Artist Alley creates its schema on first run via embedded goose migrations — there’s no separate “migrate container” or “create database” step beyond the initial role + db.

CREATE ROLE artist_alley WITH LOGIN PASSWORD 'change-me';
CREATE DATABASE artist_alley OWNER artist_alley;

The schema includes a custom plpgsql trigger for the full-text search vectors; no extensions are required beyond what ships with stock Postgres.

Two backends:

  • fs (default) — binary blobs under AA_STORAGE_FS_ROOT (the Docker image defaults this to /var/lib/aa-storage). Mount it as a volume in Docker, or a real disk on bare metal. The Go process is the only writer.
  • s3 — any S3-compatible API (AWS S3, Cloudflare R2, Backblaze B2, MinIO). See the Install guide § Configuration reference for the env vars.

Content-addressed: every blob is keyed by its SHA-256, so deduplication across the whole install is automatic. See ADR 0008 — Storage architecture.

Two things to back up:

Terminal window
# Database
pg_dump -Fc -d artist_alley > artist-alley-$(date +%Y%m%d).dump
# Filestore
rsync -a /var/lib/aa-storage/ backup-host:/srv/artist-alley/

Restore is the inverse. No proprietary export format, no “backup mode” toggle — Postgres plus a directory.

For Docker:

Terminal window
docker pull ghcr.io/artist-alley-org/artist-alley:latest
docker stop artist-alley && docker rm artist-alley
docker run -d --name artist-alley ... ghcr.io/artist-alley-org/artist-alley:latest

A systemd + native-package update path comes with the v1.0.0 distribution work (#242).

Migrations run automatically on startup. There’s no “down” path — schema changes are forward-only. ADRs (notably 0004) explain the adjunct-Postgres reasoning.

  • Health: GET /healthz (liveness probe — returns 200 when the HTTP listener + DB pool are alive).
  • Structured logs: AA_LOG_FORMAT=json for ingestable JSON, or text for humans. AA_LOG_LEVEL controls verbosity.
  • Metrics: a Prometheus-compatible scrape endpoint is on the roadmap — see the observability ADR. It isn’t in the current release.

Off by default. A fresh install is private: nothing is readable without an account. Serving content to anonymous visitors is a deliberate act by an operator, not something the software does on your behalf, and an upgrade will not switch it on.

It is enforced at the API, not by hiding pages. With it off, anonymous requests are refused — not merely unlinked or left out of navigation. That distinction is the one that matters if you are evaluating this as an internal library: there is no unlisted-but- reachable URL, because the refusal happens before a handler decides what to render.

When it is on, anonymous callers get a defined, enforced view — published, public, ready assets and public collections and posts, and nothing else. Every read path routes through a single visibility predicate rather than each endpoint deciding for itself, so there is one place to reason about (and one place to audit).

First-boot, login and SSO all work normally with it off.

With it on, anonymous visitors get a browsable site — a public front page with a curated featured rail, asset and collection browsing, and public collection pages that render their contents. Sensitivity is enforced on the bytes, not the listing, so restricted material stays visible as a locked item rather than vanishing. All of this shipped in v0.5.0.

Artist Alley is built to federate (multi-instance peering with content sync) — see the in-tree federation roadmap. Per-user prefs (theme, language, AI provider override) ride on user_profiles, which carries origin_server_id. Per-instance config (auth providers, AI providers, fonts) stays on system_config, which is local to each peer.

You can run a single instance and ignore federation entirely; the data model already accommodates it.