# Compose v2 (no "version" key needed) x-default: &default restart: unless-stopped networks: - traefik logging: driver: json-file options: max-size: 50m max-file: "2" services: headscale: <<: *default image: docker.io/headscale/headscale:latest container_name: headscale command: serve # Optional: expose metrics to host only ports: - "127.0.0.1:9090:9090" volumes: - ./config:/etc/headscale - ./lib:/var/lib/headscale - ./run:/var/run/headscale healthcheck: test: ["CMD", "headscale", "health"] interval: 30s timeout: 10s retries: 5 labels: - "traefik.enable=true" # Headscale API at / - "traefik.http.routers.headscale.rule=Host(`headscale.rozic-dev.com`)" - "traefik.http.routers.headscale.entrypoints=websecure" - "traefik.http.routers.headscale.tls.certresolver=letsencrypt" - "traefik.http.services.headscale.loadbalancer.server.port=8080" headscale-ui: <<: *default image: ghcr.io/gurucomputing/headscale-ui:latest container_name: headscale-ui # UI listens on 8080 (and 8443) in the container by default labels: - "traefik.enable=true" # Serve UI at /web on the *same* host to avoid CORS - "traefik.http.routers.headscale-ui.rule=Host(`headscale.rozic-dev.com`) && PathPrefix(`/web`)" - "traefik.http.routers.headscale-ui.entrypoints=websecure" - "traefik.http.routers.headscale-ui.tls.certresolver=letsencrypt" - "traefik.http.services.headscale-ui.loadbalancer.server.port=8080" # 🔐 BASIC AUTH MIDDLEWARE - "traefik.http.middlewares.headscale-ui-basicauth.basicauth.users=Dejan:$$2y$$05$$EAuwBj8Ac5YTRn90WSeQuezJxFZF0ghIC/mBlf6S.x8Bb/5jI49WC" # Attach middleware to router - "traefik.http.routers.headscale-ui.middlewares=headscale-ui-basicauth" networks: traefik: external: true name: traefik_default