Skip to content

Infra stack

pi-hole

  pihole:
    image: pihole/pihole:2025.08.0
    environment:
      - TZ=${TIMEZONE}
      - FTLCONF_webserver_api_password=${PIHOLE_PASS}
      - FTLCONF_dns_listeningMode=ALL
      - FTLCONF_dns_upstreams=1.1.1.1;1.0.0.1
      - VIRTUAL_HOST=pihole.${DOMAIN_NAME_SEC}
      - DNSMASQ_USER=root
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "81:80"
    volumes:
      - pihole-data:/etc/pihole
      - pihole-dnsmasq-data:/etc/dnsmasq.d
    cap_add:
      - NET_ADMIN
      - SYS_TIME
      - SYS_NICE
      - NET_BIND_SERVICE

wireguard

  wireguard:
    image: lscr.io/linuxserver/wireguard:1.0.20250521
    cap_add:
      - NET_ADMIN
      - SYS_MODULE #optional
    environment:
      - PUID=${CUID}
      - PGID=${CGID}
      - TZ=${TIMEZONE}
      - SERVERURL=${WG_SERVER_URL}
      - SERVERPORT=${WG_SERVER_PORT}
      - PEERS=5 #optional
      - PEERDNS=auto #optional
      - INTERNAL_SUBNET=10.13.13.0 #optional
      - ALLOWEDIPS=0.0.0.0/0 #optional
      - PERSISTENTKEEPALIVE_PEERS=all #optional
      - LOG_CONFS=true #optional
    ports:
      - "51820:51820/udp"
    volumes:
      - wireguard-data:/config
      - /lib/modules:/lib/modules #optional
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
      - net.ipv4.ip_forward=1

traefik

  traefik:
    image: traefik:v3.5.3
    ports:
      - "80:80"
      - "443:443"
    environment:
      #      - CF_API_EMAIL=user@example.com
      #      - CF_DNS_API_TOKEN_FILE=/run/secrets/cf_api_token
      - CLOUDFLARE_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
    secrets:
      - cf_api_token
    configs:
      - source: traefik_yaml_config
        target: /etc/traefik/traefik.yml
      - source: traefik_config_yaml_config
        target: /etc/traefik/config.yml
    volumes:
      - traefik-data:/etc/traefik
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
    deploy:
      labels:
        - "portainer.io/managed=true"
        - "io.portainer.accesscontrol.users=admin"
        - "traefik.enable=true"
        - "traefik.swarm.network=hlel"
        - "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN_NAME}`)"
        - "traefik.http.routers.traefik.entrypoints=websecure"
        - "traefik.http.routers.traefik.tls=true"
        - "traefik.http.routers.traefik.tls.certresolver=cloudflare"
        - "traefik.http.routers.traefik.tls.domains[0].main=${DOMAIN_NAME_SEC}"
        - "traefik.http.routers.traefik.tls.domains[0].sans=*.${DOMAIN_NAME_SEC}"
        - "traefik.http.routers.traefik.service=api@internal"
          # Satisfy Swarm provider: declare an explicit (unused) backend service/port.
        # It's OK because no router points to it.
        - "traefik.http.services.noop.loadbalancer.server.port=65535"

Compose

// WIP

Strategy

// WIP