Deployment
Two paths, matching the two CI image variants:
standalone/ — single-node, in-process ACME
Uses the :standalone-master image (site_encrypt manages cert lifecycle).
Suitable for VPS / bare-metal where the IRC node has direct access to ports
80 (HTTP-01 challenge) and 6697 (TLS IRC).
Two equivalent flavors of the same deployment shape:
Docker Compose (standalone/docker-compose.yml):
cd deploy/standalone
cp .env.example .env # edit values
docker compose up -d
Podman Quadlet + systemd (standalone/quadlet/) — see the README in
that directory. Useful on hosts where you’d rather have systemd manage
the unit lifecycle, journald collect logs, and rootless podman keep the
containers off privileged ports via a reverse-proxy or socket activation.
State lives in two volumes either way: *-postgres-data and
*-site-encrypt-db (the ACME cert + private key — back it up).
kubernetes/ — clustered, cert-manager-issued cert
Uses the :kubernetes-master image (site_encrypt is compiled out). Assumes
the cluster has:
- cert-manager installed with a
letsencrypt-prodClusterIssuer - an ingress controller exposing port 80 (for the HTTP-01 challenge) and a LoadBalancer / NodePort capable of fronting raw TCP for the IRC ports
- a running Postgres (the included StatefulSet is a baseline; replace it with a managed instance in real deployments)
Apply order:
kubectl apply -f deploy/kubernetes/00-namespace.yaml
kubectl apply -f deploy/kubernetes/10-rbac.yaml
kubectl apply -f deploy/kubernetes/20-postgres.yaml
# Fill in real values then:
kubectl apply -f deploy/kubernetes/30-secrets.example.yaml
kubectl apply -f deploy/kubernetes/40-certificate.yaml
kubectl apply -f deploy/kubernetes/50-services.yaml
kubectl apply -f deploy/kubernetes/60-deployment.yaml
kubectl apply -f deploy/kubernetes/70-ingress.yaml
The Certificate resource is what triggers cert-manager to obtain the
cert; once the irc-tls Secret materialises, the IRC pods mount it at
/etc/tls, and the TLSListener reads tls.crt / tls.key from there.
On renewal cert-manager updates the Secret in place, kubelet refreshes
the projected volume, and :ssl picks up the new cert on the next
handshake — no rolling restart needed.
Notes
- libcluster discovery: the headless
ircd-headlessService + RBAC in10-rbac.yamlis whatCluster.Strategy.Kubernetesuses to find peer pods.CLUSTER_STRATEGY=kubernetesenv on the Deployment activates that strategy at runtime. - Erlang cookie: must be identical across replicas; the example
Secret puts it under
RELEASE_COOKIE. - Client-IP preservation:
ircd-ircService usesexternalTrafficPolicy: Localso ban masks see real peers.