A CSI Storage Driver for Nomad (Because We Got Tired of Fighting Ours)
If you run HashiCorp Nomad in production, you already know the storage story is thinner than the one Kubernetes gets. There are fewer drivers, fewer people writing about the edge cases, and a lot of the tooling assumes a Kubernetes control plane sitting underneath. We hit the wall on this ourselves, so we built a way through it and we are open-sourcing the result: nomad-csi-driver.
It is a Container Storage Interface (CSI) driver built for Nomad, with no Kubernetes anywhere in the picture. This post covers why it exists, what it does, how we chose to build it, the gotchas worth knowing before you deploy it, and a short end-to-end walkthrough.
Why we built it
We were running democratic-csi for persistent storage on Nomad. It is a capable project, but it is written for a Kubernetes-first world, and using it on Nomad meant living with translation layers and assumptions that did not quite fit. When something broke, debugging it meant reasoning about a stack that was never really designed for the orchestrator we actually run.
We wanted something that was Nomad-native from the first line, spoke plain CSI, and covered the two storage backends we actually use every day:
A QNAP appliance serving iSCSI block LUNs to the cluster.
Node-local ZFS, for workloads that want fast local disk without a SAN in the path.
So we wrote a single Go binary that does both, and nothing we do not need.
What it does
nomad-csi-driver is one binary and one container image. You pick the backend at launch with a --driver flag:
--driver=qnap (SAN)
--driver=local (ZFS)
Storage
iSCSI block LUNs on a QNAP appliance
node-local thick ZFS zvols
Placement
reachable from any node
pinned to the node that owns the zvol
Snapshots
yes (QNAP LUN snapshot)
yes (ZFS @snap)
Clones
yes (full independent copy)
yes (send | recv)
Filesystems
ext4 / xfs (or raw block)
ext4 / xfs (or raw block)
Both backends do the same core job: provision storage, attach it to a node, and mount it for your workload. The difference is only in how they get a block device in front of the kernel. QNAP does it over iSCSI and multipath; local does it with a ZFS zvol. After that point, the exact same format and mount code runs for both. You provision with nomad volume create, you snapshot and clone through nomad volume snapshot, and you get per-volume usage stats and Prometheus metrics out of the box.
How we chose to solve it
A few decisions shaped the whole thing, and they are worth calling out because they are the reason it behaves the way it does.
One interface: CSI, and only CSI. Nomad has more than one way to attach storage now, including host volumes and the newer dynamic host volumes. We deliberately picked CSI as the single path. It is the orchestrator-standard interface, it works all the way back to our Nomad floor of v1.6.3, and it keeps us from coupling to Nomad internals that shift between releases. One interface means one mental model when something goes wrong at 2am.
The plugin is a control-plane shim, never in the data path. This is the part we are most happy with. The driver issues storage operations (provision, attach, mount, unmount) and then gets out of the way. The bytes your workload reads and writes never pass through the plugin process. The data plane lives in the host kernel: the kernel mount, the host’s iscsid, the host’s multipathd, the ZFS layer. Kill the plugin container and the iSCSI sessions, the multipath maps, and the mounts all keep running on the host.
The practical payoff is that you can upgrade, restart, or reschedule the plugin with running workloads untouched. No remounts, no I/O interruption, no forced restart of the jobs consuming the volumes. A rolling image bump is a non-event for anything already running. That is very different from drivers whose data plane lives inside the plugin process, where a restart leaves you with stale handles and forces every consumer to restart with it.
A shared format and mount layer. Because both backends converge on “here is a block device,” we wrote the format, mount, and idempotency logic once. An existing filesystem is never reformatted, a ZFS destroy is leaf-only and never recursive, and expansion is grow-only. Volumes are destroyed only when you explicitly run nomad volume delete. There is no background reconciler that can decide on its own to delete storage you did not ask it to touch.
Gotchas worth knowing before you deploy
None of these are bugs. They are design choices, and knowing them up front saves you a confused afternoon.
Single-node access only. The driver accepts every single-node CSI access mode and rejects all multi-node modes. ext4 and xfs are not cluster filesystems, so this is intentional. If you need shared read-write across nodes, this is not the tool for that volume.
QNAP volumes are whole-GiB, exactly. QNAP sizes LUNs in whole binary GiB, so the driver rejects any capacity that is not GiB-aligned rather than silently rounding it. Write capacity_min = "1GiB", not "1000MB". Local ZFS is finer-grained (it rounds up to the zvol block size).
There is no in-place rollback. CSI has no revert operation, so “restore a snapshot” means provisioning a new volume from that snapshot and repointing your job at it. True in-place zfs rollback or a QNAP revert still exists at the storage layer, but you do that out of band, outside Nomad.
You cannot delete a volume that still has snapshots. Snapshots depend on their source volume, so nomad volume delete is refused until you delete the snapshots first. Delete children, then the parent.
The local backend picks a node at create time. A local ZFS volume is pinned to one node’s pool for its whole life. You choose that node when you create it (host = "auto" for the least-provisioned capacity node, or an explicit node name). A consumer that mounts it gets scheduled onto that node automatically.
The local forwarding transport trusts the network. For --driver=local, controllers forward create and delete operations to the owning node over plain HTTP, authenticated by a shared secret in cleartext. There is no TLS in this release. Run that forwarding port on a trusted, isolated network segment and firewall it to the peer controllers. Do not expose it on a shared network. Anyone who can reach it can issue storage operations against any node.
A host reboot is different from a plugin restart. The zero-disruption property is about restarting the plugin. A full host reboot tears down the in-kernel iSCSI sessions and mounts, and everything re-stages on boot. That is expected, it is just a different kind of event.
What is out of scope
To keep this honest: the driver manages CSI volumes, not the storage systems underneath them. A couple of things are firmly on your side of the line.
Setting up the QNAP. Provisioning the appliance, creating storage pools, configuring the iSCSI portal, and creating the credentials the driver uses are all QNAP administration tasks you do before the driver ever runs.
Creating and managing zpools. For the local backend, ZFS is provided by the host. You create the zpools, keep them healthy, and decide their layout and redundancy. The driver will refuse to start on a node where its allowlisted pool is not imported and online, but it will never create or repair a pool for you. It carves zvols out of pools you already run.
If you already run QNAP and ZFS, none of this is new work. The driver just assumes that foundation exists and builds on top of it.
A short walkthrough
Here is the local (ZFS) backend end to end. The QNAP flow is nearly identical, it just uses the controller and node jobspecs instead of the single monolith job. The repo ships a complete, copy-pasteable examples/ set for both backends.
1. Deploy the plugin on every node.
nomad job run examples/local-monolith.nomad.hcl
For the local backend this runs as a system job so it lands on every node, all under one plugin id. Before you run it, edit the jobspec to point at your image, your zpool name, and your cluster’s specifics.
2. Provision a volume.
The volume spec is small and every parameter has a sensible default:
id = "local-data"
name = "local-data"
type = "csi"
plugin_id = "local"
capacity_min = "1GiB"
capacity_max = "1GiB"
capability {
access_mode = "single-node-writer"
attachment_mode = "file-system"
}
# Params below are all optional--defaults come from controller job
parameters {
pool = "tank" # a zpool from your config allowlist
host = "auto" # "auto" = fewest-volumes node, or an explicit node name
fsType = "ext4" # ext4 | xfs
volblocksize = "16K" # set at create, immutable after
}
nomad volume create examples/local-volume.hcl
3. Run a workload that mounts it.
nomad job run examples/local-consumer.nomad.hcl
The consumer mounts the volume at /data and stays up, so you can exec in and look around.
That stats endpoint reports per-volume usage (bytes, inodes, file and directory counts) keyed by the Nomad volume id you actually manage, and the same data is exposed as Prometheus gauges if you want it on a dashboard.
5. Tear down in reverse.
Stop the consumer first so the volume claim releases, then delete the volume:
That is the whole loop: deploy, create, mount, verify, delete.
Why we are sharing it
This is another piece of the same promise we make with everything at HonestHosting: no upsells, no games, just honest and simple hosting that agencies do not have to think about. Reliable persistent storage is part of that foundation, and we would rather run something we understand end to end than glue ourselves to a stack built for a different world. Open-sourcing it is the natural next step, because tools like this get better in the open.
The code, the full examples, and the deeper docs on the control-plane architecture, the metrics, and the security model all live in the repo:
If you run Nomad and you have been quietly wishing the storage story were simpler, give it a look. And if you break it in an interesting way, we would genuinely like to hear about it.
NEXT STEPS
We continue to work hard to bring you the best value for all of your WordPress hosting needs. If you have any questions, concerns, or would generally like to chat, feel free to contact us anytime. We want to make sure you get the most out of HonestHosting.io!
Turning Consul Into Your Ingress Brain: Introducing Caddy + Consul
Modern infrastructure has a persistent problem: service discovery and ingress rarely speak the same language. You end up stitching together tools like Fabio, Envoy, or custom glue just to get traffic routed correctly.
What if your ingress layer could directly understand your service registry?
This means one ingress layer for everything — web apps, APIs, and raw TCP services.
Health-Aware by Design
Routing isn’t just dynamic — it’s correct.
Only healthy services receive traffic, based on Consul’s health checks. That eliminates a whole class of failures where traffic hits dead or degraded instances.
Built for Consul Connect
If you’re using Consul Connect, the plugin integrates directly with sidecar proxies:
Honors service mesh intentions
Routes through secure sidecar paths
Works alongside your existing mesh model
This bridges the gap between service mesh and ingress, without duplicating complexity.
Zero-Restart Configuration
One of the standout features: no restarts required.
All routing updates are applied live via Caddy’s admin API. That means:
Instant propagation of new services
No downtime during deploys
No config reload race conditions
Conflict-Aware Routing
Dynamic systems can get messy — this one doesn’t.
Static config always wins
Duplicate routes resolve deterministically (“first seen wins”)
This gives you predictability without sacrificing flexibility.
Designed for Real Infrastructure
This isn’t just a plugin — it’s an architectural shift:
Your infrastructure becomes self-describing and self-routing
Consul becomes the source of truth
Caddy becomes the execution layer
The Big Idea
Instead of maintaining ingress separately from your services, this approach flips the model:
Your services define the network.
Register a service in Consul, tag it appropriately, and it’s instantly routable — HTTP, TCP, TLS, all included.
Final Thoughts
If you’re running Caddy and Nomad + Consul, this plugin removes an entire layer of operational overhead.
No more syncing configs. No more ingress drift. No more restarts.
Just declare your services — and let the network configure itself.
We continue to work hard to bring you the best value for all of your WordPress hosting needs. If you have any questions, concerns, or would generally like to chat, feel free to contact us anytime. We want to make sure you get the most out of HonestHosting.io!
Another day and another month has passed–HonestHosting continues to push the state of WordPress hosting forward into the modern age. While everyone is freaking out about AI-this, Claude-that, we are over here quietly plumbing Caddy + Consul in our primary datacenter to drop request latency by 200ms, building an easy-to-use WordPress site migrator, and fixing a bug in sidekick.
Caddy + Consul turns your service registry into a real-time ingress layer, automatically routing traffic based on Consul service registrations and health checks. It eliminates static configs and reloads by dynamically updating routes as your infrastructure changes. Compatible with Fabio-style tags, it enables easy migration while adding support for HTTP, TCP, and TLS routing in a single system. With built-in awareness of Consul Connect and service health, it ensures traffic only reaches valid, secure backends. The result is a simpler, self-configuring network where your services define how traffic flows.
The HonestHosting Site Migrator is a WordPress plugin designed to seamlessly move existing sites into the HonestHosting platform using a modern, streaming-based approach. Instead of traditional bulky exports, it breaks site data into memory-efficient, resumable chunks and uploads them directly to S3, enabling reliable migrations even for large sites.
It handles everything from preflight validation and file/database scanning to secure transfer and final import signaling, all driven from the source site itself. The process is resilient to interruptions and even supports incremental syncs over time, making ongoing migrations or updates practical.
Overall, it’s built for scalability and automation—turning WordPress migration from a fragile, manual task into a repeatable, cloud-native workflow that integrates tightly with the HonestHosting ecosystem.
A recent issue in the Honest Hosting Sidekick project uncovered a subtle but important caching flaw: responses were not properly scoped by hostname, creating a risk of cross-domain cache pollution. In multi-tenant environments, this meant one site could potentially receive cached content intended for another.
The resolution was straightforward but critical—include the request hostname in the cache key to ensure strict isolation between domains. This fix reinforces correct caching behavior and prevents unintended content leakage across sites.
It’s a strong reminder that in shared infrastructure, even small cache key decisions can have major implications for correctness and security.
NEXT STEPS
We continue to work hard to bring you the best value for all of your WordPress hosting needs. If you have any questions, concerns, or would generally like to chat, feel free to contact us anytime. We want to make sure you get the most out of HonestHosting.io!
We spent significant time fixing bugs and improving the platform to make your WordPress hosting experience smoother. Check out some of the recent changes.
STRIPE TAX COLLECTION
To better comply with tax collection, you can update your Stripe Products to include a specific tax code, which (if you are registered for tax reporting where the user is located), will be automatically collected by Stripe. We have two generic tax-codes per the Stripe Tax Guide, however, you can override these generic codes and specify your own more-specific tax code that fits your jurisdiction & business.Â
NOTE: HonestHosting is NOT a tax entity, and this is NOT tax advice. Seek counsel from a certified tax professional before changing these options so YOU understand the tax implications.
SIDEKICK BUGFIX
We recently fixed a bug in sidekick where we failed to provide contextual cache headers to downstream clients when responses contained HIT, MISS, or BYPASS. The fix makes any downstream client (CDN, Browser, etc) behave consistently, based on the existing TTL of the requested asset.
SITE ALIAS LIMITS & DOMAIN OVERRIDE
Initially, we allowed you to choose one domain for a site (example.org), and set up 3 additional aliases for it. However, you can now add up to 10 aliases, and the aliases can span different domains. Internally, we set up automatic redirects to your site’s Primary Domain if a user accesses the site using an alias (which keeps SEO and backlinks consistent).  Read more here.
WORDPRESS CORE UPDATE POLICY
By default, the HonestHosting platform automatically updates minor and dev versions of WordPress core automatically. However, you can now choose to disable any of the options (auto update major, minor, or dev versions) on a per-site basis when editing a Site under Advanced.
WORDPRESS THEME & PLUGIN UPDATE POLICY
Similar to the previous WP core update policy, the HonestHosting platform automatically updates all found themes and plugins on the site by default. However, if you want more control, you can now disable either option when editing a Site under Advanced.
SECURITY: DISALLOW_FILE_EDIT
As an additional layer of security, you can disable the built-in WordPress file editor by toggling this option in the Site Edit dialog’s Advanced section.
NEXT STEPS
We continue to work hard to bring you the best value for all of your WordPress hosting needs. If you have any questions, concerns, or would generally like to chat, feel free to contact us anytime. We want to make sure you get the most out of HonestHosting.io!
It’s been quite a while since we sent out an update; However, we have been working hard to improve the platform. Here are a few things we recently deployed.
REBRANDING
First and foremost, MyWordPress.io is renamed and rebranded to HonestHosting.io! We are super proud of the new website & branding. Check it out on the website, doc site, and app!
DOMAIN MANAGEMENT
We have partnered with name.com to allow bulk domain management. You can manage the full lifecycle of your client domains including purchase, transfer, and renewal. With HonestHosting’s promise to you (No upsells, no games), we take a nominal fee on each domain transaction and pass the rest of the savings on to you (unlike other registrars and hosts that shall remain unnamed). Read more on the doc site here.
A lightning-fast server-side static-page & cache accelerator for WordPress. Highly customizable and performant, it is generally available now, read more here.
CONTEXT AWARE CHAT
As WordPress matures into the AI era, we continue to get requests for improving our AI integration. We are testing a context-aware chat orchestrator in the HonestHosting App. It allows you to switch between platform and site context, and automatically discovers capabilities in the WordPress site. The foundation we are laying will allow us to continue growing our AI and LLM integration into the future, eventually allowing agentic site creation from conception to deployment, maintenance, and beyond. Stay tuned for more info, as we continue to build the next generation of WordPress hosting! You can also read more here, or contact us on Discord for more info.
NEXT STEPS
We continue to work hard to bring you the best value for all of your WordPress hosting needs. If you have any questions, concerns, or would generally like to chat, feel free to contact us anytime. We want to make sure you get the most out of HonestHosting.io!