Self-hosted Shlink URL shortener with the official web dashboard, backed by PostgreSQL 16. Designed to sit behind a reverse proxy that terminates TLS. - shlink server exposed on host port 8500 - shlink-web-client dashboard exposed on host port 8501 - postgres 16 kept internal, persisted via named volume - dashboard preconfigured via SHLINK_PUBLIC_URL + INITIAL_API_KEY - .env.example documents required variables; .env is gitignored Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Shlink Docker Compose
Self-hosted Shlink URL shortener with the official web dashboard, backed by PostgreSQL. Intended to run behind a reverse proxy that terminates TLS.
Stack
| Service | Image | Container port | Default host port |
|---|---|---|---|
shlink |
shlinkio/shlink:stable |
8080 |
8500 |
shlink-dashboard |
shlinkio/shlink-web-client:stable |
8080 |
8501 |
shlink-db |
postgres:16-alpine |
5432 |
not exposed |
Quick start
cp .env.example .env
# Edit .env — set DEFAULT_DOMAIN, SHLINK_PUBLIC_URL, INITIAL_API_KEY, POSTGRES_PASSWORD
docker compose up -d
Generate strong secrets:
openssl rand -hex 32 # INITIAL_API_KEY
openssl rand -hex 24 # POSTGRES_PASSWORD
Environment variables
| Variable | Required | Description |
|---|---|---|
DEFAULT_DOMAIN |
yes | Bare host used to build short URLs (e.g. s.example.com). No scheme. |
SHLINK_PUBLIC_URL |
yes | Full public URL of the Shlink API (e.g. https://s.example.com). Used by the dashboard. |
INITIAL_API_KEY |
yes | API key created on first boot. Also preconfigures the dashboard. |
POSTGRES_PASSWORD |
yes | PostgreSQL password. |
IS_HTTPS_ENABLED |
no | true (default) if your reverse proxy terminates TLS. Set false for plain HTTP. |
GEOLITE_LICENSE_KEY |
no | MaxMind GeoLite2 license key for IP geolocation. |
TIMEZONE |
no | Defaults to UTC. |
POSTGRES_DB |
no | Defaults to shlink. |
POSTGRES_USER |
no | Defaults to shlink. |
SHLINK_SERVER_PORT |
no | Host port for the Shlink API. Defaults to 8500. |
SHLINK_DASHBOARD_PORT |
no | Host port for the dashboard. Defaults to 8501. |
Reverse proxy
Two hostnames recommended:
s.example.com→http://127.0.0.1:8500— Shlink API and short URL redirects.shlink-admin.example.com→http://127.0.0.1:8501— Dashboard UI.
The proxy must terminate TLS and forward X-Forwarded-Proto / X-Forwarded-For so Shlink renders https:// short URLs and logs the correct client IP.
DEFAULT_DOMAIN must match the hostname your proxy serves the API on, otherwise generated short URLs will be wrong.
Persistence
Two named volumes:
shlink-data— Shlink cache and runtime files.shlink-db-data— PostgreSQL data directory.
Back both up before upgrades.
Common operations
docker compose logs -f shlink # follow server logs
docker compose exec shlink shlink --help # CLI inside the container
docker compose pull && docker compose up -d # upgrade images
docker compose down # stop (data preserved)
Create an additional API key:
docker compose exec shlink shlink api-key:generate
Notes
INITIAL_API_KEYonly takes effect on the very first boot, before the database is initialized. Changing it later does nothing — useapi-key:generateinstead.- The dashboard ships preconfigured with one server pointed at
SHLINK_PUBLIC_URL. The browser must be able to reach that URL; it is not a server-to-server call. - Postgres is intentionally not exposed on the host. Connect via
docker compose exec shlink-db psql -U shlink.
Description