Domain Locker logo

Domain Locker

All-in-one tool for tracking and monitoring your domain name portfolio

Alternative to: Efty, WhoisXML API

Domain Locker screenshot

Domain Locker is a self-hosted dashboard for managing your domain name portfolio in one place. It automatically fetches and tracks registrar, DNS, SSL certificate, and WHOIS data for every domain, and sends alerts before certificates or registrations expire. Includes visual analytics, change history, and multi-channel notifications via email, webhook, Slack, or Telegram.

Domain Locker Docker Compose example

Self-host Domain Locker on your own server, homelab, or VPS starting from this Docker Compose example. It runs Domain Locker in Docker containers using the official postgres:15-alpine, lissy93/domain-locker:latest, alpine:latest images, with persistent volumes and automatic restarts preconfigured. Review the environment variables and adjust them to your setup, save the file as compose.yml (or docker-compose.yml), and start the stack with docker compose up -d.

services:
  postgres:
    image: postgres:15-alpine
    restart: unless-stopped
    environment:
      # Name of the PostgreSQL database Domain Locker connects to
      POSTGRES_DB: "domain_locker"
      # PostgreSQL user that owns the database
      POSTGRES_USER: "postgres"
      # Password for the PostgreSQL user
      POSTGRES_PASSWORD: "changeme2420"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 10

  app:
    image: lissy93/domain-locker:latest
    restart: unless-stopped
    environment:
      # Deployment mode; "selfHosted" enables the local Postgres backend
      DL_ENV_TYPE: "selfHosted"
      # Hostname of the Postgres service on the compose network
      DL_PG_HOST: "postgres"
      # Port Postgres listens on
      DL_PG_PORT: "5432"
      # Postgres user Domain Locker authenticates as
      DL_PG_USER: "postgres"
      # Password for the Postgres user
      DL_PG_PASSWORD: "changeme2420"
      # Name of the Postgres database
      DL_PG_NAME: "domain_locker"
    depends_on:
      postgres:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"]
      interval: 15s
      timeout: 3s
      retries: 3
      start_period: 30s

  updater:
    image: alpine:latest
    restart: unless-stopped
    depends_on:
      app:
        condition: service_healthy
    command: >
      /bin/sh -c "
        apk add --no-cache curl &&
        echo '0 3 * * * /usr/bin/curl -f -X POST http://app:3000/api/domain-updater || echo \"ERROR: domain-updater failed at \$$(date)\" >&2' > /etc/crontabs/root &&
        echo '0 4 * * * /usr/bin/curl -f -X POST http://app:3000/api/expiration-reminders || echo \"ERROR: expiration-reminders failed at \$$(date)\" >&2' >> /etc/crontabs/root &&
        echo '*/15 * * * * /usr/bin/curl -f -X POST http://app:3000/api/domain-monitor || echo \"ERROR: domain-monitor failed at \$$(date)\" >&2' >> /etc/crontabs/root &&
        echo '0 2 * * 0 /usr/bin/curl -f -X POST http://app:3000/api/cleanup-monitor-data || echo \"ERROR: cleanup-monitor-data failed at \$$(date)\" >&2' >> /etc/crontabs/root &&
        crond -f -L /dev/stdout
      "

volumes:
  postgres_data:

Values set to changeme are required — replace them with your own values before starting Domain Locker.

Prefer a managed setup? WinterFlow installs, configures, and updates Domain Locker for you using this same Docker Compose configuration.