| title | QP Conduit Deployment Guide | ||||
|---|---|---|---|---|---|
| description | Deployment guide for QP Conduit covering Docker, air-gapped, multi-server, and production environments. Includes integration with QP Tunnel and QP Core. | ||||
| date_modified | 2026-04-04 | ||||
| ai_context | Deployment guide for QP Conduit. Docker via docker-compose.yml (port 9999), air-gapped setup (no internet), multi-server monitoring over SSH, integration with QP Tunnel (WireGuard) and QP Core (FastAPI), systemd service files, backup/restore, and upgrade procedures. | ||||
| related |
|
The simplest deployment uses Docker Compose. The included docker-compose.yml builds a multi-stage image (Node 22 for the UI, Python 3.14 for the server) and exposes the dashboard on port 9999.
# Start in development mode (foreground, with logs)
make dev
# Start in background
make go
# Stop
make stop
# View logs
make logs
# Rebuild after code changes
make refreshservices:
app:
build: . # Multi-stage Dockerfile
container_name: qp-conduit
ports:
- "9999:9999" # Dashboard + API
volumes:
- ./server.py:/app/server.py # Live reload for server changes
- ./conduit-*.sh:/app/... # Live reload for scripts
- ./lib:/app/lib # Live reload for lib modules
- ~/.config/qp-conduit:/root/.config/qp-conduit # Persistent state
- /var/run/docker.sock:/var/run/docker.sock # Container monitoring
environment:
- CONDUIT_APP_NAME=qp-conduit
- CONDUIT_CONFIG_DIR=/root/.config/qp-conduit
- CONDUIT_CADDY_ADMIN=host.docker.internal:2019
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stoppedKey design decisions:
- Shell scripts are volume-mounted so changes take effect on the next API call without rebuilding
- Config directory is mounted from the host, so state persists across container restarts
- Docker socket is mounted for container monitoring (read-only queries only)
- host.docker.internal resolves to the host machine, allowing the container to reach Caddy's admin API
Override defaults with a .env file or environment variables:
| Variable | Default | Description |
|---|---|---|
CONDUIT_APP_NAME |
qp-conduit |
Application name |
CONDUIT_CONFIG_DIR |
~/.config/qp-conduit |
Configuration directory on host |
CONDUIT_CADDY_ADMIN |
host.docker.internal:2019 |
Caddy admin API address |
CONDUIT_DOCKER_SOCKET |
/var/run/docker.sock |
Docker socket path |
Conduit operates with zero internet connectivity after initial setup. For air-gapped environments:
On a machine with internet access, download:
- Caddy binary (single static binary from caddyserver.com/download)
- dnsmasq (from your distribution's package repository)
- jq binary (single static binary from jqlang.github.io/jq/download)
- The Conduit repository (git clone or tarball)
- Python 3.14 and pip packages:
pip download -r requirements.txt -d packages/ - Node 22 and npm packages:
cd ui && npm pack(for UI pre-build)
Transfer all files to the air-gapped network via approved media (USB, optical disc, cross-domain solution).
# Install pre-staged binaries
sudo cp caddy /usr/local/bin/
sudo cp jq /usr/local/bin/
sudo apt install ./dnsmasq*.deb # or rpm, depending on distribution
# Install Python packages offline
pip install --no-index --find-links packages/ -r requirements.txt
# Pre-build the UI (or build on a staging machine and copy ui/dist/)
cd ui && npm ci --offline && npm run build && cd ..
# Initialize Conduit (no internet required)
./conduit-setup.sh --upstream-dns=127.0.0.1 # No upstream forwardingSet CONDUIT_UPSTREAM_DNS=127.0.0.1 (or any local resolver) to prevent dnsmasq from attempting upstream queries. All DNS resolution is local.
./conduit-setup.sh --domain=facility.internal --upstream-dns=127.0.0.1Conduit runs on a single gateway host but monitors multiple servers via SSH.
-
Create a dedicated monitoring user on each remote server:
sudo useradd -m -s /bin/bash conduit-monitor
-
Generate an SSH key on the Conduit gateway and distribute:
ssh-keygen -t ed25519 -f ~/.ssh/conduit-monitor -N "" ssh-copy-id -i ~/.ssh/conduit-monitor conduit-monitor@10.0.1.20
-
Monitor remote servers:
./conduit-monitor.sh --server=conduit-monitor@10.0.1.20 ./conduit-monitor.sh --server=conduit-monitor@10.0.1.30
Register services running on other machines by specifying their IP:
./conduit-register.sh --name=ollama --host=10.0.1.20 --port=11434 --health=/api/tags
./conduit-register.sh --name=vllm --host=10.0.1.30 --port=8001 --health=/healthDNS resolves these names from any machine that uses Conduit's dnsmasq as its DNS server.
Conduit and Tunnel run together on the same gateway host. Tunnel handles external access (WireGuard VPN), Conduit handles internal routing (DNS, TLS, reverse proxy).
- Set up Tunnel on the gateway host (see Tunnel documentation)
- Set up Conduit:
./conduit-setup.sh --domain=qp.local
- Register services:
./conduit-register.sh --name=core --host=127.0.0.1 --port=8000 ./conduit-register.sh --name=hub --host=127.0.0.1 --port=8090
Remote peer (WireGuard) -> Tunnel target (10.8.0.2) -> Conduit DNS + TLS -> Upstream service
Remote peers resolve core.qp.local via Conduit's dnsmasq. The request routes through Caddy with TLS termination. Caddy forwards to the upstream service.
Configure Tunnel peers to use the gateway host's IP as their DNS server. In the WireGuard client config:
[Interface]
DNS = 10.8.0.2This routes all DNS queries through the tunnel to Conduit's dnsmasq.
Register QP Core services with Conduit for DNS, TLS, and monitoring:
# QP Core API
./conduit-register.sh --name=core --host=127.0.0.1 --port=8000
# QP Hub frontend
./conduit-register.sh --name=hub --host=127.0.0.1 --port=8090
# PostgreSQL (no TLS, uses its own encryption)
./conduit-register.sh --name=postgres --host=127.0.0.1 --port=5432 --no-tls
# Redis (no TLS)
./conduit-register.sh --name=redis --host=127.0.0.1 --port=6379 --no-tls
# Ollama LLM runtime
./conduit-register.sh --name=ollama --host=127.0.0.1 --port=11434 --health=/api/tagsUpdate QP Core's environment to use Conduit DNS names:
CORE_URL=https://core.qp.local
HUB_URL=https://hub.qp.local
OLLAMA_URL=https://ollama.qp.localFor production deployments without Docker, create systemd service files:
# /etc/systemd/system/qp-conduit.service
[Unit]
Description=QP Conduit Admin Dashboard
After=network.target caddy.service
[Service]
Type=simple
User=conduit
Group=conduit
WorkingDirectory=/opt/qp-conduit
ExecStart=/usr/bin/uvicorn server:app --host 127.0.0.1 --port 9999
Restart=on-failure
RestartSec=5
Environment=CONDUIT_CONFIG_DIR=/etc/qp-conduit
Environment=CONDUIT_DIR=/opt/qp-conduit
[Install]
WantedBy=multi-user.target# /etc/systemd/system/qp-conduit-dns.service
[Unit]
Description=QP Conduit DNS (dnsmasq)
After=network.target
[Service]
Type=forking
ExecStart=/usr/sbin/dnsmasq -C /etc/qp-conduit/dnsmasq.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl enable --now qp-conduit qp-conduit-dnsAll state lives in the config directory (default: ~/.config/qp-conduit/):
| File/Directory | Purpose | Priority |
|---|---|---|
services.json |
Service registry | Critical |
audit.log |
Audit trail | Critical |
capsules.db |
Tamper-evident Capsule database | Critical |
certs/root.crt |
CA public certificate | Critical |
certs/root.key |
CA private key | Critical |
certs/*/ |
Per-service certificates | High |
conduit-hosts |
DNS hosts file | Low (regenerated) |
dnsmasq.conf |
dnsmasq config | Low (regenerated) |
Caddyfile |
Caddy config | Low (regenerated) |
routes/*.caddy |
Per-service routes | Low (regenerated) |
# Full backup
tar czf conduit-backup-$(date +%Y%m%d).tar.gz ~/.config/qp-conduit/
# Critical files only
tar czf conduit-critical-$(date +%Y%m%d).tar.gz \
~/.config/qp-conduit/services.json \
~/.config/qp-conduit/audit.log \
~/.config/qp-conduit/capsules.db \
~/.config/qp-conduit/certs/root.crt \
~/.config/qp-conduit/certs/root.keytar xzf conduit-backup-20260404.tar.gz -C /
# Regenerate derived config
./conduit-setup.sh --skip-tls # Regenerates Caddyfile and dnsmasq.conf- Back up the config directory
- Pull or download the new version
- Rebuild if using Docker:
make stop git pull make go
- For native installs:
pip install -r requirements.txt make ui-build
- Verify services are healthy:
./conduit-status.sh
Conduit does not run database migrations. The services.json format is versioned (currently version 1). Future versions will include automatic migration if the schema changes.
- Guide: Getting started walkthrough
- Network Guide: DNS and TLS trust configuration
- Architecture: System design
- Security: Security evaluation and hardening