Config reference

All services are configured via environment variables. Each service reads its own set; variables shared across services (Postgres, Redis, retention) are listed per service so every section is self-contained.

To run the stack, start from the repo-root .env.example: copy it to .env and adjust — Compose reads it automatically. Standalone runs need it sourced first: set -a; source .env; set +a.

Redirector

The Redis-only public server that 302s visitors to their Destination and records each Visit onto the Redis stream. It reads Links from Redis only, never from Postgres.

VariableDefaultPurpose
SHRL_REDIRECTOR_ADDR:8080Redirector listen address
SHRL_REDIRECTOR_RATE_LIMIT_IP600Per-IP redirects per minute; 0 disables
SHRL_REDIRECTOR_RATE_LIMIT_LINK3000Per-Link redirects per minute; 0 disables
SHRL_REDIS_ADDRlocalhost:6379Redis address
SHRL_REDIS_POOL_SIZE50Redis connection pool size
SHRL_REDIS_MIN_IDLE_CONNS5Minimum idle Redis connections

Worker

The analytics aggregator: consumes the Redis visit stream in batches and upserts daily, lifetime, and breakdown rollups into Postgres in a single transaction.

VariableDefaultPurpose
SHRL_DATABASE_URLpostgres://shrl:shrl@localhost:5432/shrlPostgreSQL connection string
SHRL_DB_MAX_OPEN_CONNS20Max open Postgres connections
SHRL_DB_MAX_IDLE_CONNS5Max idle Postgres connections
SHRL_DB_CONN_MAX_LIFETIME30mMax lifetime of a Postgres connection
SHRL_DB_CONN_MAX_IDLE_TIME5mMax idle time of a Postgres connection
SHRL_REDIS_ADDRlocalhost:6379Redis address
SHRL_REDIS_POOL_SIZE0 (auto, 10×CPU)Redis connection pool size
SHRL_REDIS_MIN_IDLE_CONNS2Minimum idle Redis connections
SHRL_RETENTION_DAYS365Analytics retention window (daily rollups)
SHRL_GEOLITE_LICENSE(unset)MaxMind license key; enables GeoIP attribution
SHRL_GEOLITE_DB_PATH/data/GeoLite2-City.mmdbPath to the GeoLite2 City database

Internal API

The API that serves the UI — reachable only by the frontend, which proxies every request on the signed-in user’s behalf and presents the session token.

VariableDefaultPurpose
SHRL_API_ADDR:8080Internal API listen address
SHRL_API_INTERNAL_SECRETdev-internal-secretShared secret the Internal API demands on every request (set to the same value on the frontend)
SHRL_ADMIN_USERNAMEadminUsername of the first-run Admin account
SHRL_ADMIN_PASSWORD(random, shown once)First-run Admin password (bcrypt-hashed)
SHRL_TOKEN_TTL86400Bearer token lifetime in seconds
SHRL_CODE_LENGTH6Seed for the per-instance Code Length setting (4–12)
SHRL_DEFAULT_BASE_URLhttp://localhost:8080Base URL auto-registered on first run and pre-selected when creating a Link
SHRL_DATABASE_URLpostgres://shrl:shrl@localhost:5432/shrlPostgreSQL connection string
SHRL_DB_MAX_OPEN_CONNS20Max open Postgres connections
SHRL_DB_MAX_IDLE_CONNS5Max idle Postgres connections
SHRL_DB_CONN_MAX_LIFETIME30mMax lifetime of a Postgres connection
SHRL_DB_CONN_MAX_IDLE_TIME5mMax idle time of a Postgres connection
SHRL_REDIS_ADDRlocalhost:6379Redis address
SHRL_REDIS_POOL_SIZE0 (auto, 10×CPU)Redis connection pool size
SHRL_REDIS_MIN_IDLE_CONNS2Minimum idle Redis connections
SHRL_RETENTION_DAYS365Analytics retention window (daily rollups)

Auth API

The public /v1 API for scripts and CI, authenticated by an API key on every request and rate-limited per IP and per key.

VariableDefaultPurpose
SHRL_AUTH_ADDR:8080Auth API listen address
SHRL_AUTH_RATE_LIMIT_IP60Per-IP requests per minute
SHRL_AUTH_RATE_LIMIT_KEY_READ300Per-key reads per minute
SHRL_AUTH_RATE_LIMIT_KEY_WRITE30Per-key writes per minute
SHRL_AUTH_RATE_LIMIT_FAIL10Failed key validations per minute per IP
SHRL_DEFAULT_BASE_URLhttp://localhost:8080Base URL pre-selected when creating a Link
SHRL_DATABASE_URLpostgres://shrl:shrl@localhost:5432/shrlPostgreSQL connection string
SHRL_DB_MAX_OPEN_CONNS20Max open Postgres connections
SHRL_DB_MAX_IDLE_CONNS5Max idle Postgres connections
SHRL_DB_CONN_MAX_LIFETIME30mMax lifetime of a Postgres connection
SHRL_DB_CONN_MAX_IDLE_TIME5mMax idle time of a Postgres connection
SHRL_REDIS_ADDRlocalhost:6379Redis address
SHRL_REDIS_POOL_SIZE0 (auto, 10×CPU)Redis connection pool size
SHRL_REDIS_MIN_IDLE_CONNS2Minimum idle Redis connections
SHRL_RETENTION_DAYS365Analytics retention window (daily rollups)

Frontend

The SvelteKit admin UI: signs users in with an HttpOnly session cookie and proxies every API call to the Internal API.

VariableDefaultPurpose
SHRL_API_URLhttp://localhost:8080Internal API address the UI proxies to
SHRL_API_INTERNAL_SECRETdev-internal-secretShared secret the Internal API demands on every request (set to the same value on the api)
SHRL_DEFAULT_BASE_URLhttp://localhost:8080Base URL pre-selected when creating a Link
SHRL_SESSION_SECRET(random per boot)HMAC secret for signing UI session cookies
SHRL_SESSION_TTL86400UI session cookie lifetime in seconds
SHRL_COOKIE_SECUREfalseSet true to send the session cookie over TLS only

Images & production notes

shrl.io publishes one image per service to ghcr.io, built for linux/amd64 and linux/arm64. The compose.yaml in the repo root assembles the full stack: these five images plus PostgreSQL and Redis.

ImageRunsPorts
ghcr.io/barats/shrl-io-apiInternal API (UI-only)none, compose-network only
ghcr.io/barats/shrl-io-authAuth API for API Keys8083
ghcr.io/barats/shrl-io-redirectRedirector8080
ghcr.io/barats/shrl-io-workerAnalytics workernone
ghcr.io/barats/shrl-io-frontendUI server8082

First run: the admin account

The first user is an Admin, provisioned on first run. Its password is the value of SHRL_ADMIN_PASSWORD if you exported one, otherwise a random value printed once to the api service logs. Change it promptly.

The two secrets

Generate SHRL_API_INTERNAL_SECRET and SHRL_SESSION_SECRET with openssl rand -hex 32, and never reuse values from another deployment. The frontend and the api must agree on the internal secret.

Quickstart

Download compose.yaml from the repo root, export the two secrets, and run podman compose up -d (or docker compose up -d). Then sign in at localhost:8082.

GeoIP attribution (optional)

Set SHRL_GEOLITE_LICENSE on the worker — a free MaxMind account — to attribute country, region, and city. Without it, locations report as unknown. The database defaults to /data/GeoLite2-City.mmdb, mounted as a volume in compose.

TLS

Serve the redirector and frontend behind an HTTPS reverse proxy, set SHRL_COOKIE_SECURE=true on the frontend, and point SHRL_DEFAULT_BASE_URL at the redirector’s public URL.

Network exposure

Keep PostgreSQL, Redis, and the Internal API off the public internet. The production compose file publishes only the redirector (:8080), the Auth API (:8083), and the frontend (:8082).

Upgrades

The compose file tracks the latest images, so podman compose pull && podman compose up -d upgrades to the newest release. To control when you upgrade, pin a release tag (e.g. :0.1.0); every release also tags its minor version (:0.1).

Pre-1.0 caution

shrl.io is pre-1.0: watch releases, pin versions when surprises are expensive, and read the release notes before upgrading — security fixes land only in the latest release. API keys never expire, so rotate them when in doubt.

This page mirrors the Configuration and Production sections of the shrl-io README, which is the canonical source.