Docker Deployment Guide

This guide covers deploying OpenGSLB using Docker containers.

Quick Start

# Pull the latest image
docker pull ghcr.io/loganrossus/opengslb:latest

# Run with default configuration
docker run -d \
  --name opengslb \
  -p 53:53/udp \
  -p 53:53/tcp \
  -p 9090:9090 \
  -v $(pwd)/config.yaml:/etc/opengslb/config.yaml:ro \
  ghcr.io/loganrossus/opengslb:latest

Image Tags

Tag

Description

latest

Most recent build from main branch

main

Same as latest

develop

Latest development build

v1.0.0

Specific version (when available)

sha-abc1234

Specific commit build

Configuration

Mounting Configuration File

Create your configuration file and mount it into the container:

# Create config directory
mkdir -p ./config

# Create configuration file
cat > ./config/config.yaml << 'EOF'
dns:
  listen_address: ":53"
  default_ttl: 60

logging:
  level: info
  format: json

metrics:
  enabled: true
  address: ":9090"

regions:
  - name: primary
    servers:
      - address: 10.0.1.10
        port: 80
    health_check:
      type: http
      interval: 30s
      timeout: 5s
      path: /health
      failure_threshold: 3
      success_threshold: 2

domains:
  - name: app.example.com
    routing_algorithm: round-robin
    regions:
      - primary
EOF

# Run with mounted config
docker run -d \
  --name opengslb \
  -p 53:53/udp \
  -p 53:53/tcp \
  -p 9090:9090 \
  -v $(pwd)/config:/etc/opengslb:ro \
  ghcr.io/loganrossus/opengslb:latest

Note: The configuration file inside the container must have secure permissions. When mounting, ensure the source file has appropriate permissions (600 or 640).

Port Mappings

Container Port

Protocol

Purpose

53

UDP

DNS queries (primary)

53

TCP

DNS queries (for large responses)

9090

TCP

Prometheus metrics

Using Non-Standard Ports

If port 53 is in use on the host:

docker run -d \
  --name opengslb \
  -p 5353:53/udp \
  -p 5353:53/tcp \
  -p 9090:9090 \
  -v $(pwd)/config:/etc/opengslb:ro \
  ghcr.io/loganrossus/opengslb:latest

Query using the mapped port:

dig @localhost -p 5353 app.example.com

Networking

Bridge Network (Default)

With the default bridge network, containers can reach external IPs but may have issues reaching host services.

# Backend on external network - works fine
servers:
  - address: 10.0.1.10
    port: 80

Host Network Mode

For production deployments or when backends run on the same host:

docker run -d \
  --name opengslb \
  --network=host \
  -v $(pwd)/config:/etc/opengslb:ro \
  ghcr.io/loganrossus/opengslb:latest

Notes:

  • Port mappings are ignored in host mode

  • OpenGSLB binds directly to host network interfaces

  • Better performance (no NAT overhead)

Accessing Host Services

To reach services running on the Docker host:

Docker Desktop (Mac/Windows):

servers:
  - address: host.docker.internal
    port: 8080

Linux:

# Get host IP from container's perspective
docker run --rm alpine ip route | grep default | awk '{print $3}'
# Use that IP in configuration

Or use host network mode.

Docker Compose

Basic Setup

# docker-compose.yml
version: '3.8'

services:
  opengslb:
    image: ghcr.io/loganrossus/opengslb:latest
    container_name: opengslb
    ports:
      - "53:53/udp"
      - "53:53/tcp"
      - "9090:9090"
    volumes:
      - ./config:/etc/opengslb:ro
    restart: unless-stopped

With Backend Services

# docker-compose.yml
version: '3.8'

services:
  opengslb:
    image: ghcr.io/loganrossus/opengslb:latest
    container_name: opengslb
    ports:
      - "53:53/udp"
      - "53:53/tcp"
      - "9090:9090"
    volumes:
      - ./config:/etc/opengslb:ro
    depends_on:
      - backend1
      - backend2
    restart: unless-stopped

  backend1:
    image: nginx:alpine
    container_name: backend1
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 10s
      timeout: 5s

  backend2:
    image: nginx:alpine
    container_name: backend2
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 10s
      timeout: 5s

With this setup, use container names in configuration:

# config/config.yaml
regions:
  - name: local
    servers:
      - address: backend1
        port: 80
      - address: backend2
        port: 80
    health_check:
      type: http
      interval: 30s
      timeout: 5s
      path: /

With Prometheus Stack

# docker-compose.yml
version: '3.8'

services:
  opengslb:
    image: ghcr.io/loganrossus/opengslb:latest
    ports:
      - "53:53/udp"
      - "53:53/tcp"
      - "9090:9090"
    volumes:
      - ./config:/etc/opengslb:ro
    restart: unless-stopped

  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9091:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
    restart: unless-stopped

  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    restart: unless-stopped

Prometheus configuration:

# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'opengslb'
    static_configs:
      - targets: ['opengslb:9090']

Health Checks

Add Docker health checks to monitor OpenGSLB:

services:
  opengslb:
    image: ghcr.io/loganrossus/opengslb:latest
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:9090/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

Or using the DNS itself:

healthcheck:
  test: ["CMD", "dig", "@localhost", "configured-domain.example.com", "+short", "+time=2"]
  interval: 30s
  timeout: 10s
  retries: 3

Resource Limits

Set resource constraints for production:

services:
  opengslb:
    image: ghcr.io/loganrossus/opengslb:latest
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 256M
        reservations:
          cpus: '0.25'
          memory: 64M

Logging

View Logs

# Follow logs
docker logs -f opengslb

# Last 100 lines
docker logs --tail 100 opengslb

# Since timestamp
docker logs --since 2024-01-01T00:00:00 opengslb

Log Drivers

For production, configure a log driver:

services:
  opengslb:
    image: ghcr.io/loganrossus/opengslb:latest
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Or send to external logging:

services:
  opengslb:
    image: ghcr.io/loganrossus/opengslb:latest
    logging:
      driver: "syslog"
      options:
        syslog-address: "tcp://logserver:514"
        tag: "opengslb"

Updating

Pull New Image

# Pull latest
docker pull ghcr.io/loganrossus/opengslb:latest

# Recreate container
docker stop opengslb
docker rm opengslb
docker run -d ... # same run command as before

With Docker Compose

docker-compose pull
docker-compose up -d

Building from Source

If you need to build the image locally:

git clone https://github.com/loganrossus/OpenGSLB.git
cd OpenGSLB

# Build image
docker build -t opengslb:local .

# Run local build
docker run -d \
  --name opengslb \
  -p 53:53/udp \
  -p 53:53/tcp \
  -p 9090:9090 \
  -v $(pwd)/config:/etc/opengslb:ro \
  opengslb:local

Troubleshooting

Container Won’t Start

# Check logs
docker logs opengslb

# Common issues:
# - Config file not mounted correctly
# - Invalid YAML in config
# - Port already in use

Cannot Reach Backends

# Enter container and test connectivity
docker exec -it opengslb sh
wget -O- http://10.0.1.10:80/health

Port 53 Conflict

On Linux, systemd-resolved often uses port 53:

# Check what's using port 53
sudo lsof -i :53

# Option 1: Disable systemd-resolved stub listener
sudo sed -i 's/#DNSStubListener=yes/DNSStubListener=no/' /etc/systemd/resolved.conf
sudo systemctl restart systemd-resolved

# Option 2: Use a different host port
docker run -p 5353:53/udp ...

Permission Denied on Config

Ensure the config file has proper permissions:

chmod 640 ./config/config.yaml

If using SELinux:

chcon -Rt svirt_sandbox_file_t ./config/

Security Considerations

  1. Run as non-root (default in the image)

  2. Read-only config mount: Use :ro flag

  3. No privileged mode: Don’t use --privileged

  4. Network policies: Restrict which networks can query DNS

  5. Resource limits: Prevent resource exhaustion attacks

Production Checklist

  • Configuration file has secure permissions (640 or 600)

  • Health checks configured

  • Resource limits set

  • Log rotation configured

  • Monitoring/alerting on metrics endpoint

  • Backup strategy for configuration

  • Update procedure documented

  • Network security (firewall rules, network policies)