# RayMail — self-hosted mail engine + webmail + telemetry # Isolation contract: binds ONLY unallocated ports; never touches host Apache, # MariaDB, or the 43 unrelated containers on this VPS. name: raymail services: stalwart: image: stalwartlabs/stalwart:v0.16.20 container_name: raymail-stalwart hostname: ${MAIL_HOSTNAME} restart: unless-stopped environment: # Pins a deterministic recovery admin instead of a one-shot random # password printed to logs. Used for the initial setup handshake. STALWART_RECOVERY_ADMIN: "admin:${STALWART_ADMIN_PASSWORD}" ports: - "25:25" # inbound MX (verified reachable from the public internet) - "465:465" # SMTP implicit TLS - "587:587" # SMTP submission STARTTLS (Outlook / Apple Mail / Thunderbird) - "993:993" # IMAP implicit TLS - "127.0.0.1:3881:8080" # admin + JMAP, loopback only — never public volumes: - ./stalwart/etc:/etc/stalwart - ./stalwart/data:/var/lib/stalwart - /etc/letsencrypt:/etc/letsencrypt:ro # certs issued by host certbot networks: [raymail] web: build: context: ./web args: NEXT_PUBLIC_APP_URL: ${APP_URL} container_name: raymail-web restart: unless-stopped env_file: .env environment: JMAP_ENDPOINT: "http://stalwart:8080" RAYMAIL_DB: "/data/raymail.db" ports: - "127.0.0.1:3880:3000" # Apache vhost proxies to this volumes: - ./data:/data # SQLite telemetry store depends_on: [stalwart] networks: [raymail] networks: raymail: name: raymail