My VPS Stack & Zero Trust Architecture
/ 15 min read
Updated: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. - GitOps Continuous Deployment via Dockhand Webhook: A GitHub Actions workflow automatically triggers Dockhand’s deployment webhook on every validated commit to the infrastructure repository, ensuring hands-off alignment without requiring direct SSH exposure.
- Systematic Healthchecks: Every critical dependency (PostgreSQL, MariaDB, MongoDB, Redis, Gitea, Kaneo, Pelican, ownCloud, ocis-qdrant-indexer, etc.) implements explicit probes (
CMD-SHELL,pg_isready,mongosh ping, HTTP/healthprobe verifying Qdrant connectivity), ensuring deterministic boot sequences usingdepends_on: service_healthyconstraints. - 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.
- ownCloud Infinite Scale (oCIS): Modern self-hosted collaborative storage and file-syncing platform. Integrated with Pocket ID via native OIDC SSO (custom claim and role mapping), it supports application tokens via the
ocis-app-tokensextension, embeds web extensions (mdpresentation-viewerfor Markdown slide presentations andunzipfor archive decompression), and leverages an Apache Tika extractor for document content analysis. Its Content Security Policy (csp.yaml) is hardened to explicitly allow Cloudflare Access assertion origins and Pocket ID IdP endpoints (frame-src,connect-src), ensuring seamless OIDC token renewal and iframe integrations without CSP violations. - 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, remote management support, and active usage statistics collection.
- Qdrant & Apache Tika: High-performance vector database and document text extraction engine dedicated to Retrieval-Augmented Generation (RAG) workflows.
- ocis-qdrant-indexer: Real-time multi-user RAG sidecar attached to the internal ownCloud Infinite Scale NATS event bus (
nats://owncloud:9233). It enforces strict per-user access isolation (dedicated Qdrant vector collections partitioned by tenant hash and filtered by authorized spaces), encrypted at-rest storage of Personal Access Tokens (PATs) in a local SQLite store (users.sqlitebacked by a dedicated persistent volume), multimodal Tika/PaddleOCR extraction, and exposes a Streamable HTTP MCP endpoint with native HTTP Basic authentication. - ocis-mcp-server: Official ownCloud Infinite Scale MCP server (
ghcr.io/frflo/ocis-mcp-server:latest) deployed in the Docker stack. It exposes 80+ tools (file navigation, share management, metadata, spaces) to AI agents and Executor with cleartext HTTP enabled internally (OCIS_MCP_INSECURE=true). - PaddleOCR CPU: High-performance optical character recognition engine (
ghcr.io/frflo/ocis-paddleocr:latest) running on the internalresourcesnetwork (internal port 8868, client timeout extended to 15 minutes). Operates alongside Apache Tika for text extraction from scanned PDFs and images. - Persistent Google Chrome: Containerized Chrome browser (
chrome) offering an interactive noVNC graphical session secured by Cloudflare Access along with a Chrome DevTools Protocol (CDP on internal port 9222) proxy for automated agent browser tasks. - GTFS MCP: Private MCP server offering transit query and spatial data analysis tools (Ilévia, SNCF, IDFM).
- Open-Meteo MCP: Internal MCP server exposing weather forecasts and historical data to Executor.
- Google Maps MCP: Internal MCP server providing geocoding, places, routes, traffic, and timezone data to Executor.
- Moodle MCP: Internal MCP server providing integration with a learning platform.
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, Backups & Monitoring
- Beszel: Central monitoring hub providing real-time telemetry on CPU, RAM, disk I/O, and container health (password authentication disabled, security fully delegated to Cloudflare Access).
- Beszel Agent: Lightweight monitoring agent deployed with a read-only Docker socket mount (
/var/run/docker.sock:ro) feeding system telemetry continuously. - Arkeep: Continuous automated backup engine for Docker volumes and system configurations (
arkeep-serverandarkeep-agent), backed by a dedicated MariaDB database. - CPA Manager Plus: Dedicated administration console and observability dashboard for the
cli-proxy-apiAI gateway. Deployed locally on port 8018 with persistent SQLite storage (/data/usage.sqlite), it tracks token consumption, response latency, and per-model quotas without interfering with live production inference. - Containerized Healthchecks: Service health probes (notably phpMyAdmin) favor direct, non-blocking TCP network socket checks (
fsockopen) over high-level HTTP wrappers, preventing false-positive instability flags during local redirects.
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 exampleingress: - hostname: ssh service: ssh://ssh - hostname: docker service: http://docker - hostname: pocket-id service: http://pocket-id - hostname: beszel service: http://beszel - hostname: pelican service: http://pelican - hostname: vert service: http://vert - hostname: pdf service: http://pdf - hostname: media service: http://media - hostname: gitea service: http://gitea - hostname: gitea-mirror service: http://gitea-mirror - hostname: phpmyadmin service: http://phpmyadmin - hostname: cli-proxy-api service: http://cli-proxy-api - hostname: owncloud service: http://owncloud - hostname: kaneo service: http://kaneo - hostname: wings service: http://wings - hostname: executor service: http://executor - hostname: spotify service: http://spotify - hostname: spotify-api service: http://spotify-api - hostname: hermes service: http://hermes - hostname: pi-bridge service: http://pi-bridge - hostname: cpa service: http://cpa-manager-plus - hostname: arkeep service: http://arkeep - hostname: chrome service: http://chrome - service: http_status:404With this architecture:
- Zero Public Listening Ports: Running
netstatorssreveals no listeners on0.0.0.0:80or0.0.0.0:443. - Edge-Level DDoS Protection: All inbound traffic is scrubbed across Cloudflare’s Anycast network before tunneling over outbound UDP.
- Strict Infrastructure Service Isolation: Internal building blocks (PostgreSQL, Redis, MongoDB, MariaDB, Qdrant vector engine, Apache Tika extractor, oCIS NATS bus, or Postfix relay) carry zero public ingress rules. They remain strictly reachable over internal Docker bridges or through the secure MASQUE mesh network.
- Strict AI Observability Segregation: The production inference endpoint, relied upon by autonomous agents, remains strictly focused on core model execution. The management and observability UI is entirely decoupled onto its own dedicated subdomain routed to local port 8018, preventing contention or disruptions to mission-critical inference pipelines.
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):
- Pocket ID: Primary self-hosted IdP enforcing FIDO2 / WebAuthn Passkeys.
- Discord OIDC: Identity broker for community bots and companion agent apps.
- 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-IdandCF-Access-Client-Secretheaders. - Targeted Bypass Rules: Selective public access for external endpoints (e.g. MCP OAuth metadata discovery).
- Public Path Bypass Pattern: For services requiring partial public exposure (such as public file sharing on ownCloud Infinite Scale), a dedicated secondary Access application is assigned to public share paths (
/s/*,/dav/public-files/*) with abypasspolicy (everyone: {}). External guests can download shared assets without prior authentication, while root and private management paths remain strictly guarded by Zero Trust. - Multi-layered Policy for Observability Consoles: For sensitive management tools such as CPA Manager Plus, Cloudflare Access enforces a layered sequence combining an Admin group
allowrule, WARP device posture verification (non_identity), and trusted IP override entries for comprehensive defense in depth. - Zero-Port SSH Access: SSH access is encapsulated over HTTPS using the
sshservice hostname, 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
Exposing Databases and Internal Services on the WARP Mesh
To reconcile direct administrative access with zero public exposure, internal services and databases bind exclusively to the IP address of the Cloudflare WARP interface (${CLOUDFLARE_MESH_IP}), preventing any socket listening on 0.0.0.0 or exposure through the L7 Cloudflare Tunnel ingress.
This segmentation follows two distinct patterns:
- Databases on their standard ports: PostgreSQL, MariaDB, Redis, and MongoDB, allowing native connections from local desktop clients (DBeaver, TablePlus, Compass) without port collisions or custom mapping.
- Internal services and MCP tooling on the
90xxport range:- Chrome DevTools / noVNC
- Arkeep Server (gRPC)
- Gitea (SSH)
- Pocket ID (internal interface)
- Wings (Pelican daemon)
- Specialized MCP servers (
google-maps-mcp,gtfs-mcp,moodle-mcp,open-meteo-mcp) ocis-qdrant-indexer(Streamable HTTP)paddleocr(OCR engine)- Postfix (SMTP relay)
- Apache Tika (document extraction)
- Qdrant (REST and gRPC endpoints)
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
applications: Hosts public web services. Containers on this bridge cannot communicate with theresourcesnetwork unless explicitly multi-homed.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.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.ymlpostfix: 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:
# Enable kernel-level packet forwardingsysctl -w net.ipv4.ip_forward=1
# Permit ingress forwarding from WARP to internal Docker bridgesiptables -I FORWARD 1 -i CloudflareWARP -d 172.18.0.0/16 -j ACCEPT
# Allow return traffic via stateful conntrack inspectioniptables -I FORWARD 2 -o CloudflareWARP -s 172.18.0.0/16 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPTWhen an administrator connects to an internal database via DBeaver across the WARP tunnel:
- The TCP SYN enters through
CloudflareWARP. - The
nf_conntrackengine registers the new flow in its state table. - The packet is routed directly to the appropriate Docker bridge.
- The service’s TCP SYN-ACK reply matches the
RELATED,ESTABLISHEDstate 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.