aller au contenu
FRFlo
Table des matières

Self-hosting modern infrastructure often turns into an uncomfortable compromise between autonomy, convenience, and security. In conventional setups, scaling up services means punching dozens of ports through public firewalls, running exposed reverse proxies subject to vulnerability scans, and hand-crafting security rules across heterogeneous services.

My production infrastructure takes a radically different route: a unified stack running over 20 Docker containers (development toolchains, project management, AI agents, game servers, databases) governed by a strict Zero Trust architecture.

The core principle? Zero application sockets listen on any public interface. Inbound requests travel over outbound QUIC tunnels, edge access requires federated multi-IdP authentication, and direct administrative access to internal resources (databases, MCP servers) runs over a private mesh network via the MASQUE protocol.

Here is the comprehensive technical breakdown of this VPS stack and its multi-layer security posture.


Infrastructure Architecture Overview

The entire stack is deployed on a dedicated Linux machine (Debian 12) hosted at OVH.

flowchart TB
    subgraph Clients["Clients & Administration Workstations"]
        Browser["Web Browser / Client App"]
        AdminDev["Admin Workstation (WARP Client)"]
        DiscordClient["Discord (Bots & Autonomous Agents)"]
    end

    subgraph Edge["Cloudflare Edge & Zero Trust"]
        Access["Cloudflare Access (IAP / IdP)"]
        Gateway["Cloudflare Gateway (L4/L7/DNS Inspection)"]
        VNet["Virtual Network (Teamnet Mesh)"]
        CFMail["smtp.mx.cloudflare.net:465"]
    end

    subgraph Host["Dedicated Server (Debian 12)"]
        subgraph Daemons["Host System Daemons"]
            CFTunnel["cloudflared (QUIC Ingress Tunnel)"]
            WARPConn["WARP Connector (MASQUE Mesh Tunnel)"]
        end

        subgraph DockerEngine["Docker Engine (vps monorepo)"]
            subgraph NetApp["Bridge: applications"]
                AppWeb["Public Web Frontends<br>Dockhand, Pocket ID, Gitea<br>Kaneo, AI Agents, Beszel Hub"]
            end

            subgraph NetRes["Bridge: resources"]
                DBs["Internal Databases<br>PostgreSQL, Redis, MariaDB, MongoDB"]
                InternalServ["Internal Daemons<br>Postfix SMTP Relay, MCP Servers"]
            end

            subgraph NetWings["Bridge: wings"]
                WingsDaemon["Pelican Wings Daemon"]
                GameContainers["Isolated Game Servers"]
            end
        end
    end

    Browser -->|HTTPS and HTTP3| Access
    Access -->|Outbound QUIC Tunnel UDP 7844| CFTunnel
    CFTunnel -->|Loopback 127.0.0.1| NetApp

    AdminDev -->|MASQUE Tunnel| Gateway
    Gateway --> VNet
    VNet -->|Private Route| WARPConn
    WARPConn -->|Netfilter conntrack| NetRes
    WARPConn -->|Netfilter conntrack| NetApp

    InternalServ -->|TLS Wrappermode port 465| CFMail

1. Stack Orchestration & Management: Dockhand

Managing two dozen containers alongside independent volumes, bind mounts, and encrypted environment files quickly turns unwieldy through raw shell commands.

To centralize management without ever exposing the Docker daemon socket (/var/run/docker.sock) to the internet, the stack is orchestrated using Dockhand, a fast and modern Docker management platform.

flowchart LR
    subgraph HostEnv["Server Host"]
        Socket["/var/run/docker.sock"]
        Compose["docker-compose.yml (vps monorepo)"]
        EnvFiles[".env (Encrypted variables)"]
    end

    subgraph DockhandUI["Dockhand Engine"]
        UI["Web Management UI"]
        Metrics["Telemetry & Event Log"]
        AutoPrune["Image Pruning & Updates"]
    end

    UI --> Socket
    UI --> Compose
    UI --> EnvFiles

Core Orchestration Highlights:

  • Unified Declarative Stack (vps): Every service, virtual network, and storage volume is declared within a structured, version-controlled Docker Compose file.
  • Systematic Healthchecks: Every critical dependency (PostgreSQL, MariaDB, MongoDB, Redis, Gitea, Kaneo, Pelican, etc.) implements explicit probes (CMD-SHELL, pg_isready, mongosh ping), ensuring deterministic boot sequences using depends_on: service_healthy.
  • Automated Lifecycle & Hygiene: Automated image digest polling (latest, lts) paired with periodic automated orphan image pruning.
  • Isolated Control Plane: Dockhand itself runs in an isolated container backed by a dedicated volume.

2. Application Catalog: Service Overview

The stack serves everyday needs across multiple domains: development toolchains, identity management, productivity, AI workflows, gaming, and analytics.

A. Identity, Authentication & Security

  • Pocket ID: Modern, lightweight OIDC identity provider written in Go and Svelte. Natively supports Passkeys (FIDO2 / WebAuthn) for passwordless, hardware-backed biometric sign-ins.
  • phpMyAdmin: Web-based administration interface for internal MariaDB databases.

B. Development, GitOps & CI/CD

  • Gitea: Self-hosted Git forge backed by PostgreSQL for state storage and Postfix for transactional mail notifications.
  • Gitea Mirror: Automated service continuously mirroring remote GitHub repositories onto the local Gitea instance to maintain sovereign, offline backups.
  • act-runner: Native CI/CD runner executing GitHub Actions workflows directly on the server without external dependencies.

C. Productivity & Project Management

  • Kaneo: Modern project and issue tracker (a fluid alternative to Linear or Jira), integrated with Pocket ID via OAuth/OIDC using PKCE flows.
  • Pingvin Share: Privacy-first file sharing platform offering client-side encryption and time-limited download links.
  • BentoPDF: Full-featured web utility suite for processing and editing PDF documents.

D. AI Ecosystem, Agents & MCP

  • Executor: Secure sandboxed code runtime and Model Context Protocol (MCP) server, offering controlled execution primitives for AI agents.
  • Hermes Agent: Autonomous AI agent (powered by NousResearch Hermes) acting as a Discord administration bot, connected through the CLI Proxy API gateway.
  • Pi Bridge: Communication and remote execution gateway for the Pi Coding Agent, synchronized in real time with Discord.
  • CLI Proxy API: Gateway proxy standardizing LLM requests with Service Token authorization.
  • GTFS MCP: Private MCP server offering transit query and spatial data analysis tools.

E. Gaming & Game Server Virtualization

  • Pelican Panel: Next-generation game server management interface (the modern successor to Pterodactyl), connected to PostgreSQL and Postfix.
  • Pelican Wings: Host daemon governing the lifecycle of game server containers (Minecraft Bedrock, SteamCMD dedicated servers) on an isolated bridge network.

F. Media & Utilities

  • Cobalt: Private, privacy-preserving media download and extraction instance.
  • VERT: High-performance CPU-accelerated video conversion engine.
  • Your Spotify: Personal analytics dashboard tracking Spotify listening history and habits over a MongoDB datastore.

G. Observability & Monitoring

  • Beszel: Central monitoring hub providing real-time telemetry on CPU, RAM, disk I/O, and container health.
  • Beszel Agent: Lightweight monitoring agent deployed with a read-only Docker socket mount (/var/run/docker.sock:ro) feeding system telemetry continuously.

3. Layer 7 Ingress: Cloudflare Tunnel & QUIC

To make these applications reachable from the web without opening a single incoming HTTP or HTTPS port on the server’s firewall, Ingress relies on Cloudflare Tunnel (cloudflared).

sequenceDiagram
    participant Edge as Cloudflare Edge (PoPs)
    participant Cloudflared as cloudflared Daemon (Host)
    participant LocalApp as Docker Container (127.0.0.1:local_port)

    Note over Cloudflared,Edge: Boot: 4 outbound QUIC tunnels to the Edge
    Cloudflared->>Edge: Outbound persistent connection (UDP/7844)
    Edge-->>Cloudflared: Tunnels established & active

    Note over Edge: Inbound user request for a service
    Edge->>Cloudflared: Request multiplexed inside QUIC stream
    Cloudflared->>LocalApp: Local HTTP reverse proxy to 127.0.0.1
    LocalApp-->>Cloudflared: HTTP 200 OK Response
    Cloudflared-->>Edge: Packet returned via QUIC stream

Secured Ingress Mechanics

Every web application container binds strictly to loopback 127.0.0.1 on an unexposed port:

# cloudflared Ingress rules example
ingress:
- hostname: auth.example.com
service: http://127.0.0.1:8001
- hostname: git.example.com
service: http://127.0.0.1:8007
- hostname: tasks.example.com
service: http://127.0.0.1:8012
- service: http_status:404

With this architecture:

  • Zero Public Listening Ports: Running netstat or ss reveals no listeners on 0.0.0.0:80 or 0.0.0.0:443.
  • Edge-Level DDoS Protection: All inbound traffic is scrubbed across Cloudflare’s Anycast network before tunneling over outbound UDP.

4. Authentication & IAP: Multi-IdP Identity Broker

Application endpoints are guarded behind Cloudflare Access (Identity Aware Proxy).

sequenceDiagram
    participant Client as Web Browser
    participant Access as Cloudflare Access (IAP)
    participant OIDCProvider as OIDC Provider
    participant Service as Web Application

    Client->>Access: Navigates to application URL
    Access->>Client: Redirect to Identity Broker
    Client->>OIDCProvider: Authenticates (Passkeys / WebAuthn)
    OIDCProvider-->>Access: Signed OIDC assertion
    Note over Access: Evaluates policies + signs JWT RS256
    Access->>Client: Encrypted session cookie
    Access->>Service: Injects Cf-Access-Jwt-Assertion header
    Service-->>Client: Renders application

Multi-Provider Federation (IdPs):

  1. Pocket ID: Primary self-hosted IdP enforcing FIDO2 / WebAuthn Passkeys.
  2. Discord OIDC: Identity broker for community bots and companion agent apps.
  3. GitHub & Google OAuth: Fallback identities for emergencies and recovery.

Granular Policies & Service Tokens:

  • Admin Policy (allow): Unrestricted access for the verified administrator.
  • WARP Device Posture (non_identity): Transparent single-click pass-through for trusted machines enrolled with a verified client certificate.
  • Machine-to-Machine Service Tokens: Automated script access via CF-Access-Client-Id and CF-Access-Client-Secret headers.
  • Targeted Bypass Rules: Selective public access for external endpoints (e.g. MCP OAuth metadata discovery).
  • Zero-Port SSH Access: SSH access is encapsulated over HTTPS using cloudflared access ssh --hostname ssh.example.com, completely removing port 22 from the public internet.

5. Layer 4 Mesh Network: WARP Connector & the MASQUE Protocol

To interact directly with backend datastores (PostgreSQL, MariaDB, Redis, MongoDB) or private MCP servers using local desktop tools (DBeaver, TablePlus, VS Code), the host runs a WARP Connector joined to a Cloudflare Virtual Network (Teamnet).

flowchart LR
    subgraph DevMachine["Developer Workstation (WARP Client)"]
        DBeaver["DBeaver / TablePlus (SQL)"]
        MongoCompass["MongoDB Compass"]
        MCPClient["MCP Client / VS Code"]
    end

    subgraph CFMesh["Cloudflare Virtual Network"]
        VNet["Private Virtual Network"]
        CGNAT["CGNAT Range: 100.64.0.0/10"]
    end

    subgraph VPS["Host Server (WARP Connector)"]
        WARPInt["CloudflareWARP Interface (MASQUE)"]
        Netfilter["Netfilter / conntrack"]
        
        subgraph Subnets["Announced Docker Subnets"]
            SubRes["resources network (Databases)"]
            SubApp["applications network (Tools & MCP)"]
            SubWings["wings network (Game servers)"]
        end
    end

    DBeaver -->|TCP SQL| WARPInt
    MongoCompass -->|TCP NoSQL| WARPInt
    MCPClient -->|HTTP MCP| WARPInt
    WARPInt --> Netfilter
    Netfilter --> SubRes
    Netfilter --> SubApp

The MASQUE Protocol (RFC 9298 / RFC 9484)

The mesh tunnel operates via MASQUE (Multiplexed Application Substrate over QUIC Encryption), tunneling raw IP packets (TCP and UDP) inside HTTP/3 over QUIC streams.

Compared to legacy VPN protocols (like standard WireGuard):

  • Traffic seamlessly negotiates corporate firewalls and restrictive proxies, masquerading as regular outbound HTTPS on port 443.
  • QUIC multiplexing prevents head-of-line blocking across concurrent database queries and admin sessions.

Client-Side Split Tunneling (Include Mode):

Only targeted destination IP blocks enter the tunnel:

  • Private Docker subnets on the host
  • Cloudflare WARP CGNAT range: 100.64.0.0/10 (RFC 6598)
  • Specified internal domain zones

6. Docker Micro-Segmentation: 3 Isolated Bridge Networks

To strictly limit the blast radius if any single application container is compromised, the environment is segmented into three independent bridge networks:

flowchart TD
    subgraph NetApp["Bridge: applications"]
        A1["Dockhand"]
        A2["Pocket ID"]
        A3["Gitea"]
        A4["Kaneo"]
        A5["Executor MCP"]
    end

    subgraph NetRes["Bridge: resources"]
        R1[("PostgreSQL")]
        R2[("Redis")]
        R3[("MariaDB")]
        R4[("MongoDB")]
        R5["Postfix SMTP Relay"]
    end

    subgraph NetWings["Bridge: wings"]
        W1["Pelican Wings Daemon"]
        W2["Dedicated Game Servers"]
    end

    A3 -->|SQL Queries| R1
    A4 -->|SQL Queries| R1
    A2 -->|SQL Queries| R1
    A3 -->|Outbound SMTP| R5
    A2 -->|Outbound SMTP| R5
    W1 -->|SQL Queries| R3
  1. applications: Hosts public web services. Containers on this bridge cannot communicate with the resources network unless explicitly multi-homed.
  2. resources: Houses internal databases and backend tools. No ports are mapped to the host; connections originate exclusively from authorized application containers or through the WARP mesh tunnel.
  3. wings: Dedicated entirely to gaming containers created by Pelican Wings, completely walled off from production databases and developer tooling.

7. Secure SMTP Egress via Cloudflare Email Routing

Transactional emails (account verification, Git notifications, system health alerts) bypass unencrypted or risky direct SMTP relays:

sequenceDiagram
    participant App as App Container (Gitea / Pocket ID)
    participant Postfix as Postfix Relay Container (Internal)
    participant CFMail as Cloudflare Email (smtp.mx.cloudflare.net:465)
    participant Recipient as Final Recipient

    App->>Postfix: Internal plaintext SMTP (port 25)
    Note over Postfix: TLS Wrappermode + SASL Auth (API Token)
    Postfix->>CFMail: TLS Connection to port 465
    CFMail-->>Recipient: Delivered with SPF / DKIM / DMARC signatures

The internal postfix container operates inside the resources bridge and securely forwards all outbound traffic to [smtp.mx.cloudflare.net]:465 using TLS wrappermode and a scoped Cloudflare API Token.

# Postfix service definition in docker-compose.yml
postfix:
container_name: postfix
image: mwader/postfix-relay:latest
networks:
- resources
environment:
- POSTFIX_mynetworks=10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8, 100.64.0.0/10
- POSTFIX_relayhost=[smtp.mx.cloudflare.net]:465
- POSTFIX_smtp_tls_security_level=encrypt
- POSTFIX_smtp_tls_wrappermode=yes
- POSTFIX_smtp_sasl_auth_enable=yes
- POSTFIX_smtp_sasl_password_maps=hash:/etc/postfix/sasl_passwd
- POSTMAP_sasl_passwd=[smtp.mx.cloudflare.net]:465 api_token:${CF_SMTP_API_TOKEN}

8. Linux Kernel Tuning: Netfilter, Conntrack & Routing

Bridging the WARP Connector virtual interface (CloudflareWARP) with internal Docker bridges requires the Linux kernel’s connection tracking subsystem (nf_conntrack) combined with IPv4 packet forwarding:

Terminal window
# Enable kernel-level packet forwarding
sysctl -w net.ipv4.ip_forward=1
# Permit ingress forwarding from WARP to internal Docker bridges
iptables -I FORWARD 1 -i CloudflareWARP -d 172.18.0.0/16 -j ACCEPT
# Allow return traffic via stateful conntrack inspection
iptables -I FORWARD 2 -o CloudflareWARP -s 172.18.0.0/16 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

When an administrator connects to an internal database via DBeaver across the WARP tunnel:

  1. The TCP SYN enters through CloudflareWARP.
  2. The nf_conntrack engine registers the new flow in its state table.
  3. The packet is routed directly to the appropriate Docker bridge.
  4. The service’s TCP SYN-ACK reply matches the RELATED,ESTABLISHED state rule and is returned automatically without public port exposure.

9. Gateway Filtering: DNS, HTTP & L4 Network Security

Upstream of the server, every connection is continuously screened by Cloudflare Gateway policies:

Layer Policy Rule Action Result
DNS Anti-Malware Block Prevents resolution of known malicious domains
DNS Anti-Phishing Block Mitigates credential harvesting attempts
DNS Trusted Allowlist Allow Prioritizes DNSSEC-validated trusted endpoints
HTTP Threat Inspection Block Detects and intercepts malicious payload URLs
L4 / Network Malware & Phishing Drop Block Immediate packet drop on flagged FQDN destinations

Conclusion

This infrastructure demonstrates that self-hosting a comprehensive, highly productive suite of services does not require sacrificing security:

  • Uncompromised Productivity: Over 20 containers handling dev, AI, gaming, and project workflows, orchestrated cleanly through Dockhand.
  • Zero Public Attack Surface: No web or SSH ports open to the public internet. Port scans encounter an impenetrable wall.
  • Continuous Identity Verification: Every request is verified through Pocket ID (Passkeys) and Cloudflare Access with cryptographic JWT assertions.
  • Transparent Remote Engineering: Administrative access to databases and backend tools relies on MASQUE mesh tunnels with zero friction and zero bastion servers.

A robust, modern setup built to last.