How to Self-Host Shlink with Docker Compose

Shlink is a modern, API-first URL shortener with a clean web client. It supports custom slugs, device-specific redirects, tag-based organization, and detailed visit analytics — all built-in without plugins. QR codes are generated via the web client. Unlike YOURLS which is PHP/jQuery, Shlink is built on PHP 8 with Swoole for high performance and ships with a React-based web client. Official site

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose installed (guide)
  • 500 MB of free disk space
  • 256 MB of RAM minimum
  • A domain name for your short URLs

Docker Compose Configuration

Create a docker-compose.yml file:

services:
  shlink:
    image: shlinkio/shlink:5.0.1
    container_name: shlink
    environment:
      - DEFAULT_DOMAIN=sho.rt              # Your short domain
      - IS_HTTPS_ENABLED=true              # Set true if behind HTTPS proxy
      - INITIAL_API_KEY=change-this-to-a-strong-api-key
      - DB_DRIVER=maria
      - DB_HOST=shlink_db
      - DB_USER=shlink
      - DB_PASSWORD=change-this-db-password
      - DB_NAME=shlink
    ports:
      - "8080:8080"
    depends_on:
      - shlink_db
    restart: unless-stopped

  shlink_db:
    image: mariadb:11.7
    container_name: shlink_db
    environment:
      - MYSQL_ROOT_PASSWORD=change-this-root-password
      - MYSQL_DATABASE=shlink
      - MYSQL_USER=shlink
      - MYSQL_PASSWORD=change-this-db-password
    volumes:
      - db-data:/var/lib/mysql
    restart: unless-stopped

  shlink-web:
    image: shlinkio/shlink-web-client:4.7.0
    container_name: shlink-web
    environment:
      - SHLINK_SERVER_URL=https://sho.rt   # URL of your Shlink server
      - SHLINK_SERVER_API_KEY=change-this-to-a-strong-api-key
    ports:
      - "8081:8080"
    restart: unless-stopped

volumes:
  db-data:

Environment variables (Shlink server):

VariablePurposeRequired
DEFAULT_DOMAINYour short URL domainYes — must change
IS_HTTPS_ENABLEDEnable HTTPS URL generationYes
INITIAL_API_KEYAPI key for authenticationYes — must change
DB_DRIVERDatabase type (maria, postgres, sqlite)Yes
DB_HOSTDatabase hostnameYes (except SQLite)
DB_USERDatabase usernameYes (except SQLite)
DB_PASSWORDDatabase passwordYes — must change

Start the stack:

docker compose up -d

Initial Setup

  1. Shlink server starts on port 8080 — this handles redirects
  2. Web client starts on port 8081 — this is the management UI
  3. Open http://your-server-ip:8081 for the web client
  4. The web client auto-connects using the configured API key
  5. Start creating short URLs

Configuration

Creating Short URLs

Via the web client:

  • Enter the long URL
  • Optionally set a custom slug, title, tags
  • Set expiration date or maximum visits
  • Enable/disable bot visit tracking

Via the API:

curl -X POST "https://sho.rt/rest/v3/short-urls" \
  -H "X-Api-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"longUrl": "https://example.com/very/long/url", "customSlug": "docs"}'

Device-Specific Redirects

Shlink can redirect to different URLs based on the visitor’s device:

curl -X POST "https://sho.rt/rest/v3/short-urls" \
  -H "X-Api-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "longUrl": "https://example.com/desktop",
    "deviceLongUrls": {
      "android": "https://play.google.com/store/apps/details?id=com.example",
      "ios": "https://apps.apple.com/app/example/id123456"
    }
  }'

QR Codes

As of Shlink 5.0, QR code generation is handled by the Shlink Web Client (Dashboard) rather than the server. Open any short URL in the web client to generate and download QR codes with customizable size, format (PNG/SVG), margin, and colors.

Tags and Domains

Organize short URLs with tags for filtering and analytics. Shlink also supports multiple domains — serve different short URL sets from different domains, all managed by one instance.

Advanced Configuration (Optional)

GeoIP for Visit Location

Enable geographic visit tracking:

environment:
  - GEOLITE_LICENSE_KEY=your-maxmind-key  # Free from maxmind.com

Sign up for a free MaxMind account to get a GeoLite2 license key. Shlink downloads the GeoIP database automatically.

Redis for Better Performance

Add Redis for caching and improved performance under load:

  shlink_redis:
    image: redis:7-alpine
    container_name: shlink_redis
    restart: unless-stopped

Add to Shlink’s environment:

  - REDIS_SERVERS=shlink_redis:6379

RabbitMQ for Async Processing

For high-traffic instances, offload visit processing:

environment:
  - RABBITMQ_ENABLED=true
  - RABBITMQ_HOST=rabbitmq
  - RABBITMQ_PORT=5672

Multiple Short Domains

environment:
  - DEFAULT_DOMAIN=sho.rt
  # Additional domains configured via API after startup

Use the API to add more domains: each domain can have its own set of short URLs.

Reverse Proxy

Your reverse proxy should send traffic to:

  • Port 8080 (Shlink server): For the short domain (redirects + API)
  • Port 8081 (Web client): For the management interface

Example: sho.rt → port 8080, admin.sho.rt → port 8081

Reverse Proxy Setup

Backup

docker compose exec shlink_db mariadb-dump -u shlink -p shlink > shlink-backup-$(date +%Y%m%d).sql

Backup Strategy

Troubleshooting

Short URLs Return “URL Not Found”

Symptom: Created URLs return a 404 page. Fix: Ensure DEFAULT_DOMAIN matches the domain visitors use. If behind a reverse proxy, IS_HTTPS_ENABLED=true must be set when the proxy terminates HTTPS. Check that the reverse proxy forwards to port 8080.

Web Client Can’t Connect to Server

Symptom: Web client shows “Could not connect to server.” Fix: Verify SHLINK_SERVER_URL in the web client matches the publicly accessible URL of the Shlink server. The web client connects from the browser, not from the Docker network — it needs the external URL, not an internal hostname.

API Key Not Working

Symptom: API calls return 401 Unauthorized. Fix: INITIAL_API_KEY only creates the key on first startup. If you change it after the database is initialized, the old key remains. Create a new key via the CLI: docker compose exec shlink shlink api-key:generate.

GeoIP Data Not Loading

Symptom: Visit locations show as “Unknown.” Fix: Set GEOLITE_LICENSE_KEY with a valid MaxMind key. The database downloads on startup — check logs for download errors. MaxMind rate-limits downloads to ~1 per day.

Resource Requirements

  • RAM: ~100 MB (Shlink) + 100 MB (MariaDB) + 50 MB (web client)
  • CPU: Low
  • Disk: ~50 MB for application, grows with visit analytics data

Verdict

Shlink is the best URL shortener for developers and API-driven workflows. The QR code generation (via the web client), device-specific redirects, and multi-domain support are features that YOURLS only gets through plugins. The separate web client means you can run the API server on your short domain and the management UI on a different domain or behind authentication. For a simpler, more established option, YOURLS is the alternative.

FAQ

Shlink: modern API-first design, QR codes via web client, device redirects, React dashboard. YOURLS: older but proven, larger plugin ecosystem, simpler single-container setup. Choose Shlink for API integration and modern features; YOURLS for simplicity and stability.

Shlink has an import command: docker compose exec shlink shlink short-url:import bitly. It requires a Bitly API token. Historical visit data does not transfer — only the URLs themselves.

No. The API server works standalone — you can create and manage URLs entirely via the API or CLI. The web client is a convenience, not a requirement.

Comments