How to Self-Host LinkAce with Docker Compose

LinkAce doesn’t just store bookmarks — it monitors them. It periodically checks whether your saved URLs are still alive and flags dead links. Combined with tags, lists, and a REST API, it’s a capable self-hosted alternative to Raindrop.io and Pinboard. The stack is heavier than some alternatives (PHP/Laravel with MariaDB, Redis, and nginx), but the link monitoring feature justifies the overhead if dead link detection matters to you. linkace.org

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose installed (guide)
  • 512 MB of free RAM (4 containers: app, nginx, MariaDB, Redis)
  • A domain name (recommended for remote access)

Docker Compose Configuration

LinkAce requires four services: the PHP application, an nginx web server, MariaDB, and Redis. Create a docker-compose.yml:

services:
  db:
    image: mariadb:11.2
    container_name: linkace-db
    restart: unless-stopped
    command: mariadbd --character-set-server=utf8mb4 --collation-server=utf8mb4_bin
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_USER: ${DB_USERNAME}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_DATABASE: ${DB_DATABASE}
    volumes:
      - linkace-db:/var/lib/mysql

  app:
    image: linkace/linkace:v2.5.1
    container_name: linkace-app
    restart: unless-stopped
    depends_on:
      - db
    volumes:
      - ./.env:/app/.env
      - ./backups:/app/storage/app/backups
      - linkace-app:/app
      - linkace-logs:/app/storage/logs

  nginx:
    image: bitnami/nginx:1.24
    container_name: linkace-nginx
    restart: unless-stopped
    ports:
      - "80:8080"
    depends_on:
      - app
    volumes:
      - linkace-app:/app
      - ./nginx.conf:/opt/bitnami/nginx/conf/server_blocks/linkace.conf:ro

  redis:
    image: bitnami/redis:7.2
    container_name: linkace-redis
    restart: unless-stopped
    environment:
      REDIS_PASSWORD: ${REDIS_PASSWORD}

volumes:
  linkace-app:
  linkace-logs:
  linkace-db:

Create a .env file with your configuration:

# --- Application ---
APP_KEY=base64:GENERATE_THIS_KEY
APP_URL=http://localhost
APP_ENV=production
APP_DEBUG=false
APP_TIMEZONE=UTC

# --- Database ---
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=linkace
DB_USERNAME=linkace
DB_PASSWORD=change-this-strong-password

# --- Redis ---
REDIS_HOST=redis
REDIS_PASSWORD=change-this-redis-password
REDIS_PORT=6379

# --- Cache & Sessions (use Redis) ---
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_DRIVER=database

# --- Email (optional, for notifications) ---
MAIL_MAILER=log
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=null
MAIL_PASSWORD=null

# --- Link checking ---
META_GENERATION_TIMEOUT=10

# --- Reverse proxy ---
TRUSTED_PROXIES=*

Generate the APP_KEY:

docker run --rm linkace/linkace php artisan key:generate --show

Copy the base64:... output into your .env file.

Create the nginx.conf file in the same directory. Download it from the LinkAce release (included in linkace-docker.zip), or create it manually:

server {
    listen 0.0.0.0:8080;
    root /app/public;
    index index.php;

    client_max_body_size 20M;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass app:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

Start everything:

docker compose up -d

Initial Setup

  1. Open http://your-server in your browser
  2. The web-based setup wizard appears — it configures the database and creates your admin account
  3. Follow the prompts: confirm database settings, set your admin email and password
  4. After setup, you’re logged into the dashboard

LinkAce is ready to use. Start adding bookmarks via the web UI, browser extension, or API.

Configuration

Setting.env VariableDefaultNotes
Base URLAPP_URLhttp://localhostChange to your actual domain
TimezoneAPP_TIMEZONEUTCPHP timezone string
Session lifetimeSESSION_LIFETIME10080 (7 days)Minutes
Metadata timeoutMETA_GENERATION_TIMEOUT10Seconds to wait when fetching link titles/descriptions
Automated backupsBACKUP_ENABLEDfalseEnable for scheduled database backups
Queue driverQUEUE_DRIVERdatabaseHandles background jobs (link checking, metadata fetching)

LinkAce’s standout feature is automatic dead link detection. The queue worker checks saved URLs periodically and flags broken links (404s, timeouts, DNS failures) in the dashboard. The check runs as a background job via the queue driver.

To ensure link checking works:

  1. Set QUEUE_DRIVER=database in .env
  2. The built-in supervisor process handles queue workers automatically
  3. Dead links appear with a warning indicator in your bookmark list

Reverse Proxy

When running behind an external reverse proxy (Nginx Proxy Manager, Caddy, Traefik), proxy to linkace-nginx:8080. Set these headers in your proxy config:

proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;

Set APP_URL to your public HTTPS URL and TRUSTED_PROXIES=* in .env. See Reverse Proxy Setup.

Backup

LinkAce includes built-in backup support. Enable it in .env:

BACKUP_ENABLED=true
BACKUP_DISK=local_backups

Backups are stored in the ./backups bind mount. You can also back up the MariaDB database directly:

docker compose exec db mariadb-dump -u linkace -p linkace > linkace-backup.sql

See Backup Strategy.

Troubleshooting

”500 Server Error” after setup

Symptom: The app shows a generic 500 error after initial setup or during use. Fix: Check APP_KEY is set correctly in .env. Run: docker compose exec app php artisan config:clear. Check logs: docker compose logs app and look in the linkace-logs volume.

Setup wizard won’t complete

Symptom: The database configuration step hangs or fails. Fix: Verify DB_HOST=db matches the service name. Ensure MariaDB has started: docker compose logs db. The utf8mb4_bin collation is required — the command: in the compose file sets this.

”Class not found” or autoload errors

Symptom: PHP class errors appear in logs. Fix: The linkace-app named volume may have stale files from a previous version. Remove the volume and recreate: docker compose down -v && docker compose up -d. This forces a fresh application deploy.

Symptom: Bookmarked links never get checked for dead status. Fix: Ensure QUEUE_DRIVER=database is set. The queue worker runs via supervisor inside the app container. Check: docker compose exec app php artisan queue:work --once to manually trigger a job.

Redis connection refused

Symptom: Errors about Redis connection failures in logs. Fix: Verify REDIS_PASSWORD in .env matches what’s set for the Redis service. The bitnami Redis image requires REDIS_PASSWORD to be set in both the Redis container and the app’s .env.

Resource Requirements

ResourceValue
RAM (total, all 4 containers)250–400 MB idle
CPULow — single core sufficient
Disk (application)~500 MB
Disk (data)Grows slowly with bookmarks; minimal per-link storage
Containers4 (app, nginx, MariaDB, Redis)

The 4-container architecture makes LinkAce heavier than simpler alternatives like Linkding or Shiori.

Verdict

LinkAce is worth the setup complexity if dead link detection matters to you. The automated link monitoring — periodically checking whether your saved URLs still resolve — is a feature most bookmark managers skip entirely. Tags, lists, a REST API, and browser extensions round out a capable tool.

But if you don’t need link monitoring, Linkding is the better choice for most people. It’s a single container, uses SQLite, and has a cleaner UI. Linkwarden offers more features (screenshots, collaboration) if you want something richer. LinkAce sits in between — more capable than Linkding, less polished than Linkwarden.

FAQ

Can I use PostgreSQL instead of MariaDB?

Yes. Set DB_CONNECTION=pgsql in .env and replace the MariaDB service with PostgreSQL. The official compose file includes a commented-out PostgreSQL service as a reference. Most users stick with MariaDB since that’s the primary tested configuration.

Does LinkAce have a browser extension?

Yes — extensions are available for Firefox and Chrome/Edge. They let you save the current page as a bookmark with one click. Install from your browser’s extension store and point it at your LinkAce instance URL.

How does LinkAce compare to Linkding?

Linkding is simpler (single container, SQLite), lighter, and faster to set up. LinkAce has link monitoring, more organizational features (lists in addition to tags), and a REST API. Choose Linkding for simplicity; choose LinkAce for link health monitoring. See LinkAce vs Linkding.

Comments