Channels, back planes, and the infrastructure underneath.
When people learn about building services, they focus on the ingress side. How do I accept connections? How do I parse requests? How do I handle load? Good questions. But only half the picture.
A service that can only receive is a dead end. Real systems are conversations. Your service receives a request, processes it, and then talks to other things — databases, queues, other services, hardware. The send side is just as important as the receive side, and the patterns you use for it change everything about reliability, latency, and debuggability.
This page starts with the protocol patterns — control channels, back channels, push vs pull. Then it goes deeper: what happens when the most powerful channel is the infrastructure itself — when storage, networking, and observability are kernel primitives baked into the image, and your services get capabilities they never asked for.
Part 1: Channels and control planes
Here's a pattern that shows up everywhere, and once you see it, you can't unsee it: active and passive communication.
FTP: The original two-channel protocol
FTP uses two connections: a control channel (port 21) where commands are sent, and a data channel (a separate port) where actual file data flows. In active mode, the server connects back to the client on a port the client specified. In passive mode, the client connects to a port the server specified.
This separation of control and data is everywhere. VDI protocols (Citrix ICA, VMware PCoIP) use a control channel for session management and separate channels for display, audio, USB, and clipboard — each optimized differently. Kubernetes uses the API server as a control plane and kubelet connections as the data plane.
The back channel: reverse communication
Sometimes the most powerful pattern is the one that goes backwards. Instead of the client always initiating, the server pushes. Instead of polling, you subscribe. Instead of the installer pulling packages from the internet, the ISO carries everything with it.
An installer is nothing more than a process where hardware and services
communicate to accomplish a feat. And the smartest installers use back channels —
the target system reporting its state back to the installer, the bootloader telling the
init system what to mount, the kernel passing parameters to userspace through /proc/cmdline.
Part 2: The back plane
The back plane is the part you usually have to build yourself.
Every working API developer already understands that production services have a back plane — a side-band channel for the things the request/response path shouldn't handle. Replication. Encryption in transit. Snapshots. Metrics. Audit. Disaster recovery. The question isn't whether you know the back plane exists. It's where it lives.
On most stacks, the back plane is a separate stack of products you provision next to your service: an APM agent, a backup daemon, a VPN client, a sidecar for mTLS, an object-storage SDK for off-host copies, a Prometheus exporter, a log forwarder. Each one is a dependency to install, a config file to keep in sync, a thing to forget on the staging box, a thing to break on a deploy. Your API code ends up coordinating across them — signalling the backup service, signalling the replication service, signalling the metrics endpoint — each over its own protocol.
kldload moves the back plane into the kernel. The same primitives are already there because they were compiled in at image build time. Your code signals ZFS for a snapshot, WireGuard for an encrypted destination, eBPF for a probe, Cilium for a network policy — not a daemon wrapping them, the kernel module itself. There's no agent to crash, no sidecar to scale, no config drift between staging and prod. Same kernel, same signals, every machine.
Think about what's happening underneath a typical web service:
Front plane (your API):
POST /api/upload → accept file → store → return 200
Back plane (what the infrastructure handles):
→ ZFS checksums every block as it's written (data integrity)
→ ZFS compresses the data transparently (storage efficiency)
→ Snapshot fires automatically before the write (undo capability)
→ WireGuard encrypts the replication stream (secure distribution)
→ zfs send pushes the dataset to DR site (disaster recovery)
→ eBPF traces the write latency (observability)
Your API wrote one line: store(file). The back plane did six things
your API never asked for, never configured, and can't break.
This is what "baked into the image" actually means. When the back plane lives in the kernel — not in a sidecar, not in an agent, not in a config file that gets missed during deployment — every application on the machine gets it for free. Your API doesn't import a ZFS library or call a snapshot endpoint. When it does want to reach into the back plane explicitly, it signals the kernel module directly: zfs snapshot, zfs send over a WireGuard peer, an eBPF probe attached to a syscall, a Cilium policy applied to a label. The signal goes to the kernel, not to a userland coordinator that wraps it.
Now think about what you can add to your API when you know the back plane exists:
# Your API can now offer these features with zero additional infrastructure:
# Point-in-time recovery for any customer
GET /api/customer/acme/restore?snapshot=2026-04-01T14:00
→ zfs rollback rpool/customers/acme@2026-04-01T14:00
# Instant staging environment from production data
POST /api/staging/create?from=production
→ zfs clone rpool/production@latest rpool/staging/ticket-4521
→ returns in 0.2 seconds regardless of dataset size
# Encrypted replication to customer's own infrastructure
POST /api/customer/acme/replicate?target=acme-dr.example.com
→ zfs send | ssh wg-peer zfs recv
→ your customer gets a verified copy, never decrypted in transit
# Real-time I/O observability without APM agents
GET /api/debug/slow-queries
→ eBPF traces disk latency per dataset, returns top offenders
None of these features required a new dependency. No new database. No new queue. No new service to deploy and monitor. They're API wrappers around kernel primitives that already exist on the machine because they were baked into the image at build time.
This is the power of embedding the back plane at image creation: every machine you deploy has these capabilities from second zero. You don't configure them per-host. You don't install them post-deploy. You don't forget them on the staging server. They're structural. They're in the image. They're everywhere, always.
What this unlocks for API and database developers
If you build APIs or run databases for a living, the practical effect of kernel-resident ZFS, WireGuard, eBPF, and Cilium is that side-band features that normally require a stack of products become callable primitives. The actions below are not abstractions kldload invented — they are the unmodified kernel-module commands, available to any process on the host with the right capability bits. Your API handler, your stored procedure, your background worker, or your cron job can fire them directly.
Per-request snapshot & rollback
An endpoint that mutates customer state can snapshot the dataset before the mutation, in-band, and roll back on failure — no out-of-band backup service, no nightly window.
POST /api/migrate/acme
zfs snapshot tank/db/acme@pre-migrate-$req
try: run_migration()
except: zfs rollback tank/db/acme@pre-migrate-$req
finally: keep snapshot for audit
DBA equivalent: each BEGIN ... COMMIT now has a filesystem-level companion that survives the database process dying.
Zero-cost staging from production data
Clone a production dataset for QA, a customer support replay, or a migration dry-run in milliseconds. The clone shares blocks with prod until it diverges — no extra disk, no pg_dump, no anonymisation pass on cold data.
POST /api/staging/from-prod
ts=$(date +%s)
zfs snapshot tank/db/prod@stg-$ts
zfs clone tank/db/prod@stg-$ts tank/db/stg-$ts
pg_ctl -D /tank/db/stg-$ts -o "-p 55$ts" start
return { dsn: "postgres://stg:55$ts/app" }
A 4 TB Postgres cluster cloned in < 1 s; staging is real production state, not a sanitised reconstruction of it.
Tenant-isolated replication
Issue a customer their own encrypted replication stream over a WireGuard peer your API created on the fly — the receiving side stores ciphertext only.
POST /api/customer/acme/replicate-key
wg set wg0 peer $pub allowed-ips 10.99.42.7/32
zfs send -R -w tank/db/acme@latest \
| ssh -i acme.key 10.99.42.7 zfs recv tank/from-vendor
-w sends raw (encrypted) blocks; the customer host never holds your dataset's key. BYOK + customer-owned DR with no third party in the chain.
Point-in-time recovery as an API verb
Hourly sanoid snapshots become a recovery API for any tenant, any time horizon — without a backup product.
GET /api/customer/acme/restore?t=2026-05-25T14:00
zfs rollback tank/db/acme@autosnap_2026-05-25-1400_hourly
systemctl restart postgres@acme
RPO matches your snapshot policy (15 min / hourly / daily). RTO is measured in seconds, not the size of the dataset.
On-demand kernel observability
A diagnostic endpoint that attaches an eBPF probe for the duration of the call — no APM agent, no instrumentation library, no redeploy.
GET /api/debug/io-hotspots?seconds=10
timeout 10 biolatency-bpfcc -j > /tmp/h.json
return parse(/tmp/h.json) # histogram per disk
DBAs: pair with dbslower / tcpconnect / execsnoop for kernel-side trace of a misbehaving query that the slow-log can't see.
Per-tenant network policy as a write
Cilium policies are Kubernetes CRDs, but the underlying datapath is a kernel program. Your control-plane API can write a policy that takes effect in the kernel in milliseconds — not a reconfig-and-reload of an external firewall.
POST /api/tenant/acme/quarantine
kubectl apply -f - <<EOF
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata: { name: quarantine-acme }
spec:
endpointSelector: { matchLabels: { tenant: acme } }
egress: []
EOF
A compromise-detection signal triggers an API call; the offending tenant is network-isolated by the kernel before the next packet leaves.
Per-dataset storage tuning, per-service
Each microservice gets its own ZFS dataset with the right recordsize, compression, logbias, primarycache, and quota. No "everything on one ext4 with one block size".
zfs create -o recordsize=8k -o logbias=latency tank/pg
zfs create -o recordsize=16k -o primarycache=metadata tank/redis
zfs create -o recordsize=1M -o compression=zstd tank/media
zfs create -o quota=50G -o reservation=10G tank/audit
DBAs: recordsize=8k matches the Postgres page size so a single read fetches exactly one page. No write amplification at the storage layer.
Atomic deploy / instant rollback
A deploy is a snapshot, a switch, and (if it fails) a one-line rollback — not a multi-step orchestrator and a backup restore.
kbe new pre-deploy-$(git rev-parse HEAD)
deploy_new_version || kbe rollback pre-deploy-$(git rev-parse HEAD)
Boot environment is the entire OS state — binaries, configs, libraries, the kernel itself. Rollback returns the host to the exact moment before the deploy.
Instrument your own API in the kernel — export to Grafana in ~10 lines
Attach a uprobe to your own binary's functions. Latency, call count, args, exit code — captured kernel-side, no APM SDK, no recompile, no service restart. Pipe the bpftrace map into the bundled Prometheus exporter and the dashboard fills itself.
# /etc/bpftrace/api-latency.bt — ~10 lines, runs as a systemd unit
uprobe:/srv/app/bin/api:handle_request {
@start[tid] = nsecs;
}
uretprobe:/srv/app/bin/api:handle_request {
$lat_us = (nsecs - @start[tid]) / 1000;
@lat = hist($lat_us); # histogram → /metrics
@rps = count(); # rps → /metrics
delete(@start[tid]);
}
interval:s:10 { print(@lat); print(@rps); clear(@lat); clear(@rps); }
Now kldload-exporter publishes api_request_latency_us + api_requests_per_second on :9100; the pre-wired Grafana dashboard at :3000 picks them up automatically. No code change to the API. No agent in the request path. Detach the probe and the overhead is zero again.
Same pattern works for: Postgres backend functions (uprobe on libpq), Redis commands (uprobe on processCommand), nginx handlers, Java JVM methods via the JVMTI bridge, Python functions via libpython symbols. Anything with debug symbols can be traced — in production — without touching the application.
Protocol brokers: the right tool for the job
Ansible is great at push. "Go to these ten machines and run this playbook." But it's stateless — it doesn't know what happened between runs. Salt is a protocol broker. It maintains persistent connections (ZeroMQ under the hood), receives events in real-time, and can react. It does what Ansible does, plus everything Ansible can't.
The trick isn't choosing one. It's understanding that push and pull, active and passive, request-reply and pub-sub — these are all tools. A hammer and a screwdriver aren't competing. You use the one that fits. The person who only has a hammer thinks everything is a nail. The person who understands the toolbox builds things that work.
So that's how services talk to each other — control channels, back channels, push and pull. But there's a deeper question: what channel does the infrastructure itself provide? What if the most powerful back channel isn't one your application creates, but one the kernel gives you for free?
The image is the deployment. The deployment is the infrastructure.
Traditional infrastructure has stages: provision the machine, install the OS, configure networking, add storage, deploy the app, bolt on monitoring, set up backups. Each stage is a separate tool, a separate config, a separate failure mode. Miss one and you have a machine that's 90% right — which is the same as wrong.
When you bake the back plane into the image, the stages collapse:
Traditional:
provision → install OS → configure networking → add storage
→ deploy app → add monitoring → configure backups → harden
→ pray nothing was missed
(8 stages, 8 tools, 8 places to make a mistake)
kldload:
boot the image
(the image IS the OS + networking + storage + snapshot policy + security hardening)
→ deploy your app
(2 stages. The first one is turning the machine on.)
This isn't about saving time, although it does. It's about eliminating the gaps between stages where things go wrong. The machine that doesn't have monitoring because someone forgot the Ansible role. The server that doesn't have backups because the cron job wasn't in the golden image. The production box running without encryption because the WireGuard config was "TODO."
When it's in the image, it's everywhere. When it's a post-deploy step, it's everywhere you remembered to put it.
Part 3: Datasets as service boundaries
Datasets are service boundaries. Design them that way.
Any service that manipulates files — an upload processor, a transcoder, a log aggregator, a CI runner — is reading and writing to a path. That path is a directory. On ext4, all directories share one filesystem, one set of I/O characteristics, one failure domain. A runaway log writer starves the upload processor. A transcoder's sequential 4GB writes thrash the CI runner's random 4K reads. Everything competes for the same disk, the same cache, the same throughput.
On ZFS, you separate them into datasets. The service doesn't know the difference — it still reads and writes to a path. But now each path has its own tuning:
# Upload processor — large incoming files, compress well, limit space
zfs create -o recordsize=1M -o compression=zstd -o quota=500G \
rpool/srv/uploads
# Transcoder — huge sequential reads/writes, skip compression (already encoded)
zfs create -o recordsize=1M -o compression=off -o atime=off \
rpool/srv/transcode
# Log aggregator — append-only, compress heavily, rotate via snapshots
zfs create -o recordsize=1M -o compression=zstd-19 -o atime=off \
rpool/srv/logs
# CI runner — small random I/O, fast metadata, throw away between builds
zfs create -o recordsize=16K -o compression=lz4 -o sync=disabled \
rpool/srv/ci-workspace
# Database — tuned to page size, protect every write
zfs create -o recordsize=8K -o logbias=throughput -o compression=lz4 \
rpool/srv/postgres
Each service gets I/O characteristics matched to its workload. The transcoder's 1M sequential writes don't pollute the database's 8K random reads in the ARC. The CI runner's throwaway workspace has sync=disabled because losing it on power failure doesn't matter — but the database has full sync because every write matters. Never use sync=disabled on anything you can't afford to lose on power failure. The log aggregator compresses at zstd-19 because it's write-once-read-rarely and saves 80% of disk space.
Now the interesting part — you can optimize the routing between them:
# User uploads a video. Traditional approach:
# write to /tmp → copy to /uploads → copy to /transcode → copy to /output
# Three copies of a 4GB file. 12GB of I/O for one upload.
# ZFS approach:
# write to rpool/srv/uploads (one write)
# zfs clone rpool/srv/uploads@snap rpool/srv/transcode/job-123 (instant, zero I/O)
# transcode in place on the clone
# zfs send the result to rpool/srv/output (block-level, only changed blocks)
# destroy the clone
# One write. One clone (free). One incremental send. Total I/O: ~4GB instead of 12GB.
# The upload processor, transcoder, and output service never copied a file.
# They worked on datasets. ZFS moved the blocks.
This is what happens when you stop thinking of services as programs that read files and start thinking of them as consumers of datasets. The file manipulation becomes a dataset operation. The copy becomes a clone. The transfer becomes a send. The backup is a snapshot that already happened. You cut the I/O in half or better, and every stage is atomic, checksummed, and reversible.
The services never know. They read a path. They write to a path. But underneath, each path is a tuned, isolated, snapshotted, replicable storage domain — and the data flows between them at the block level instead of being copied through userland.