Shared Storage & Workload Parity
In a traditional Kubernetes setup, different workloads (Web, Workers, Scheduler) often have isolated filesystems. This leads to common issues in Laravel applications, such as a sitemap generated by the Scheduler not being visible to the Web server, or temporary files from a job being inaccessible to other workers.
LaraKube CLI solves this by implementing a Unified Shared Storage architecture.
π The Unified Volume Modelβ
Instead of separate Persistent Volume Claims (PVCs) for each service, LaraKube CLI uses a single, high-performance PVC that is shared across all Laravel-related pods in your project namespace.
π What is shared?β
The following directories are mounted from the shared volume into every pod (web, horizon, scheduler, reverb, etc.):
storage/: All application logs, file uploads, and framework-managed data.bootstrap/cache/: Shared framework optimization files. This ensures that when the Web pod runsartisan optimize, all workers immediately benefit from the cached configuration.app/public/: Assets that are generated dynamically (like sitemaps, QR codes, or processed images) are immediately available to the web server.
π Workload Parityβ
This architecture ensures "Workload Parity"βthe guarantee that all parts of your application see the exact same filesystem state at all times.
| Feature | Without Shared Storage | With LaraKube CLI Shared Storage |
|---|---|---|
| Sitemaps | Hidden in the Scheduler pod. | Instantly served by the Web pod. |
| Log Tailing | Must check individual pods. | Centralized in the shared volume. |
| Config Cache | Can get out of sync. | Unified in bootstrap/cache. |
| File Uploads | Requires external S3. | Works seamlessly with local storage. |
The table above describes one node where every pod shares a single volume β the default on a single-node VPS. At multi-node scale a block volume can't span nodes, so the model changes. See Storage across the scaling journey below: externalize by default, opt in to a shared filesystem only when an app truly needs one.
πΊ Storage across the scaling journeyβ
How the shared volume behaves depends on where you run. On a single node it's automatic; at multi-node scale you choose between externalizing state (recommended) and an opt-in shared filesystem.
1. Single-node (the $6β12 VPS) β shared by defaultβ
On a Single-Node Hero, every pod lands on the same node, so LaraKube CLI mounts one ReadWriteOnce (RWO) PVC and shares it across web, horizon, scheduler, and friends. You get full Workload Parity for free, at native disk speed, with no object storage required. This is the default and the sweet spot for most apps.
2. Multi-node (HA) β externalize state (recommended)β
When you graduate to a multi-node managed cluster (multi-node-ha), block storage (do-block-storage, EBS, β¦) is ReadWriteOnce β it physically cannot span nodes. So LaraKube CLI runs the app pods stateless: each gets a per-pod emptyDir (no shared PVC) and is free to schedule on any node.
State then lives outside the pods:
- Uploads /
app/publicβ object storage (DO Spaces, S3, or a Plex Commons running MinIO/SeaweedFS). - Sessions / cache β Redis (or Memcached, or the database).
larakube cloud:externalize <env> does this in one guided step β it flips the env's
FILESYSTEM_DISK/SESSION_DRIVER/CACHE_STORE/QUEUE_CONNECTION and wires the backends from whichever
source you choose: a Plex Commons, a self-hosted
MinIO/Redis (via larakube add), or your own managed services β without clobbering existing connection
values. cloud:deploy also offers to run it when it finds local state. SQLite stays single-node by
nature (its DB is a file), so it prompts you to switch to a networked engine first. See
cloud:externalize.
Why externalize instead of a shared disk? It's the only path that's genuinely HA β no shared-storage single point of failure, pods reschedule anywhere, and it's how managed platforms expect Laravel to scale.
3. Multi-node with a real shared folder β opt-in NFS (experimental)β
Some apps genuinely need a shared cross-node filesystem β e.g. a worker writes public/storage/sitemap.xml and the web pod serves it, and rewriting that to S3 isn't worth it. For those, opt in per environment:
larakube cloud:provision:nfs # installs an in-cluster NFS provisioner β larakube-nfs StorageClass
# then set "sharedStorage": true on the env, and redeploy
LaraKube CLI stands up a single NFS server (a block volume re-exported as ReadWriteMany) and points the shared PVC at it, so your shared folder works across nodes unchanged.
The NFS server is a single pod: a storage single point of failure (your app pods stay HA, but if that pod restarts, shared I/O pauses). It also relies on the node's NFS mount path, which does not work on DigitalOcean Kubernetes (DOKS) β the mount hangs. Treat this as an escape hatch for apps that can't externalize; prefer option 2 wherever you can.
4. Truly-HA shared filesystem β advanced (roadmap)β
A production-grade ReadWriteMany without the single-pod SPOF needs a distributed filesystem β CephFS / Longhorn / csi-driver-nfs or a managed filer. LaraKube CLI doesn't wire these up yet; it's a tracked roadmap item for teams that need both a shared folder and high availability.
π» Local developmentβ
Local provisioners usually only support ReadWriteOnce (RWO). To maintain parity, LaraKube CLI uses a Surgical Kustomize Patch (overlays/local/pvc-patch.yaml) that pins PVCs to RWO. Since your local cluster is a single node, multiple pods still mount the same RWO volume simultaneously β exactly like the single-node production path.