version: '3.3' services: # Reverse Proxy - Caddy for routing all .cell traffic caddy: image: git.pic.ngo/roof/pic-caddy:latest container_name: cell-caddy profiles: ["core", "full"] ports: - "80:80" - "443:443" volumes: - ./config/caddy/Caddyfile:/etc/caddy/Caddyfile - ./data/caddy:/data - ./config/caddy/certs:/config/caddy/certs restart: unless-stopped cap_add: - NET_ADMIN networks: cell-network: ipv4_address: ${CADDY_IP:-172.20.0.2} logging: driver: json-file options: max-size: "10m" max-file: "5" # DNS Server - CoreDNS for .cell TLD resolution dns: image: coredns/coredns:latest container_name: cell-dns profiles: ["core", "full"] command: ["-conf", "/etc/coredns/Corefile"] ports: - "${DNS_PORT:-53}:53/udp" - "${DNS_PORT:-53}:53/tcp" volumes: - ./config/dns/Corefile:/etc/coredns/Corefile - ./data/dns:/data restart: unless-stopped networks: cell-network: ipv4_address: ${DNS_IP:-172.20.0.3} logging: driver: json-file options: max-size: "10m" max-file: "5" # DHCP Server - dnsmasq for IP leasing dhcp: image: alpine:latest container_name: cell-dhcp profiles: ["core", "full"] ports: - "${DHCP_PORT:-67}:67/udp" volumes: - ./config/dhcp/dnsmasq.conf:/etc/dnsmasq.conf - ./data/dhcp:/var/lib/misc restart: unless-stopped networks: cell-network: ipv4_address: ${DHCP_IP:-172.20.0.4} command: ["/bin/sh", "-c", "apk add --no-cache dnsmasq && dnsmasq -d -C /etc/dnsmasq.conf"] cap_add: - NET_ADMIN logging: driver: json-file options: max-size: "10m" max-file: "5" # NTP Server - chrony for time synchronization ntp: image: alpine:latest container_name: cell-ntp profiles: ["core", "full"] ports: - "${NTP_PORT:-123}:123/udp" volumes: - ./config/ntp/chrony.conf:/etc/chrony/chrony.conf restart: unless-stopped networks: cell-network: ipv4_address: ${NTP_IP:-172.20.0.5} cap_add: - SYS_TIME command: ["/bin/sh", "-c", "apk add --no-cache chrony && rm -f /var/run/chrony/chronyd.pid && exec chronyd -d -f /etc/chrony/chrony.conf -n"] logging: driver: json-file options: max-size: "10m" max-file: "5" # WireGuard VPN wireguard: image: linuxserver/wireguard:latest container_name: cell-wireguard profiles: ["core", "full"] environment: - SERVERMODE=true - PUID=${PUID:-1000} - PGID=${PGID:-1000} ports: - "${WG_PORT:-51820}:${WG_PORT:-51820}/udp" volumes: - ./config/wireguard:/config - /lib/modules:/lib/modules restart: unless-stopped networks: cell-network: ipv4_address: ${WG_IP:-172.20.0.9} cap_add: - NET_ADMIN - SYS_MODULE privileged: true sysctls: - net.ipv4.conf.all.src_valid_mark=1 - net.ipv4.ip_forward=1 - net.ipv4.conf.all.rp_filter=0 logging: driver: json-file options: max-size: "10m" max-file: "5" # API Server api: build: ./api container_name: cell-api profiles: ["core", "full"] ports: - "127.0.0.1:${API_PORT:-3000}:3000" environment: - DDNS_URL=${DDNS_URL:-https://ddns.pic.ngo/api/v1} - DDNS_TOTP_SECRET=${DDNS_TOTP_SECRET:-S6UMA464YIKM74QHXWL5WELDIO3HFZ6K} volumes: - ./data/api:/app/data - ./data/dns:/app/data/dns - ./config/api:/app/config - ./config/caddy:/app/config-caddy - ./config/wireguard:/app/config/wireguard - ./config/dns:/app/config/dns - ./data/logs:/app/api/data/logs - /var/run/docker.sock:/var/run/docker.sock - ./.env:/app/.env.compose - ./docker-compose.yml:/app/docker-compose.yml:ro - ./docker-compose.services.yml:/app/docker-compose.services.yml - ./scripts:/app/scripts:ro pid: host restart: unless-stopped networks: cell-network: ipv4_address: ${API_IP:-172.20.0.10} depends_on: - wireguard - dns logging: driver: json-file options: max-size: "10m" max-file: "5" # Web UI - React + Vite webui: build: ./webui container_name: cell-webui profiles: ["core", "full"] ports: - "${WEBUI_PORT:-8081}:80" restart: unless-stopped networks: cell-network: ipv4_address: ${WEBUI_IP:-172.20.0.11} logging: driver: json-file options: max-size: "10m" max-file: "5" networks: cell-network: name: cell-network external: true