version: '3.3' services: # Reverse Proxy - Caddy for routing all .cell traffic caddy: image: caddy:2-alpine container_name: cell-caddy 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 command: ["-conf", "/etc/coredns/Corefile"] ports: - "53:53/udp" - "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 ports: - "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 ports: - "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" # Email Server - Postfix + Dovecot mail: image: mailserver/docker-mailserver:latest container_name: cell-mail hostname: mail domainname: cell.local env_file: ./config/mail/mailserver.env ports: - "25:25" - "587:587" - "993:993" volumes: - ./data/maildata:/var/mail - ./data/mailstate:/var/mail-state - ./data/maillogs:/var/log/mail - ./config/mail/config:/tmp/docker-mailserver/ - ./config/mail/ssl:/etc/letsencrypt restart: unless-stopped networks: cell-network: ipv4_address: ${MAIL_IP:-172.20.0.6} cap_add: - NET_ADMIN logging: driver: json-file options: max-size: "10m" max-file: "5" # Calendar & Contacts - Radicale radicale: image: tomsquest/docker-radicale:latest container_name: cell-radicale ports: - "5232:5232" volumes: - ./config/radicale:/etc/radicale - ./data/radicale:/data restart: unless-stopped networks: cell-network: ipv4_address: ${RADICALE_IP:-172.20.0.7} logging: driver: json-file options: max-size: "10m" max-file: "5" # File Storage - WebDAV webdav: image: bytemark/webdav:latest container_name: cell-webdav ports: - "8080:80" environment: - AUTH_TYPE=Basic - USERNAME=admin - PASSWORD=admin123 volumes: - ./data/files:/var/lib/dav restart: unless-stopped networks: cell-network: ipv4_address: ${WEBDAV_IP:-172.20.0.8} logging: driver: json-file options: max-size: "10m" max-file: "5" # WireGuard VPN wireguard: image: linuxserver/wireguard:latest container_name: cell-wireguard environment: - SERVERMODE=true - PUID=${PUID:-1000} - PGID=${PGID:-1000} ports: - "51820: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 sysctls: - net.ipv4.conf.all.src_valid_mark=1 - net.ipv4.ip_forward=1 logging: driver: json-file options: max-size: "10m" max-file: "5" # API Server api: build: ./api container_name: cell-api ports: - "3000:3000" volumes: - ./data/api:/app/data - ./data/dns:/app/data/dns - ./config/api:/app/config - ./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 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 ports: - "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" # Webmail - RainLoop rainloop: image: hardware/rainloop container_name: cell-rainloop restart: unless-stopped networks: cell-network: ipv4_address: ${RAINLOOP_IP:-172.20.0.12} ports: - "8888:8888" volumes: - ./data/rainloop:/rainloop/data logging: driver: json-file options: max-size: "10m" max-file: "5" # File Manager - FileGator filegator: image: filegator/filegator container_name: cell-filegator restart: unless-stopped networks: cell-network: ipv4_address: ${FILEGATOR_IP:-172.20.0.13} ports: - "8082:8080" volumes: - ./data/filegator:/var/www/filegator/private logging: driver: json-file options: max-size: "10m" max-file: "5" networks: cell-network: driver: bridge ipam: config: - subnet: ${CELL_NETWORK:-172.20.0.0/16}