/tmp and /run,
and persistent volumes that are fixed-size ext4 filesystems preallocated on
the host. Writes past the capacity fail inside the service with ENOSPC
instead of filling the host.
Services that do not opt in keep exactly their previous behavior. Upgrading
Crewship does not recreate them or change their containers.
Declare a quota-enforced service
In a crew manifest:quota_bytes and generation are only accepted on quota-enforced services.
The same fields are editable in Crew Settings → Service disk quotas.
A quota-enforced service:
- runs with a read-only image root;
/tmp(64 MiB) and/run(16 MiB) are tmpfs mounts charged to the memory limit. Both start empty and are writable by any user (sticky, like/tmp): directories the image created under/run(for example/var/run/postgresql) are not carried over, and the image’s entrypoint must create them, as the official database images do; - gets 2 GiB of memory with no extra swap, 1 CPU and 512 processes;
- keeps at most 30 MB of rotated
json-filelogs (this overrides the daemon’s default log driver for this service only); - may not use image-declared volumes it has not listed: if the image declares
VOLUME /var/lib/postgresql/data, list that path undervolumes; - cannot mount a volume at
/,/tmp,/run,/var/run,/dev,/procor/sys.
USER (for example
USER 1001) and handed over empty, without lost+found, so initialisers such
as initdb accept it. Images that name their user (USER postgres) get a
root-owned volume root; the official database images start as root and fix
ownership in their entrypoint.
Change capacity: bump the generation
A generation’s capacity never changes. To resize, set a new generation with the newquota_bytes. The new generation is a new, empty filesystem: the
service is recreated on it, and the data on the previous generation is not
copied. Migrate it yourself (for example pg_dump from the old service before
the switch, restore after).
Changing quota_bytes while keeping the generation is rejected when you save
(400 from the API, a plan error from crewship apply) with the generation to
use.
Old generations are not deleted when you switch: they keep their disk
reservation so you can still read from them. They are released when the crew is
deleted. There is no command to release a single old generation yet, so plan
host capacity for both generations during a migration.
Restarts
A quota-enforced service without a persisted intent restarts on failure through Docker, like any other service. Once you persist an intent withcrewship crew service-state, the
durable service controller owns its restarts and Docker’s restart policy is
disabled, so a stopped service stays stopped across Docker and host restarts.
Backups
Crew, workspace and whole-instance backups include quota volumes when the server’s quota helper is configured. The backup stops the service and copies each volume offline; the service controller resumes it after the complete bundle is written. Without the helper, a backup that covers a crew with quota volumes is refused with an explicit error rather than producing a bundle that silently omits them. If a backup fails while copying these volumes, the service stays stopped under maintenance. Once the failed backup’s writers have stopped, an admin retries withcrewship backup create --recover-services. See
Backup.
Host requirements
Quota services need, on the Docker host:- Swap and PID cgroup accounting.
docker infomust report swap limit and PID limit support. Without it Docker accepts the limits but does not enforce them, so Crewship refuses to start the service and names the missing controller. On cgroup v1 hosts, swap accounting needs theswapaccount=1kernel parameter. - The quota helper, for services with volumes. Without it a quota service with volumes does not start; it never falls back to unlimited storage. A quota service without volumes needs only the cgroup support above.
Install the helper
The helper is a small root process that owns the volume images. Each Crewship database gets its own helper instance, catalog and socket. Install it as root on the Docker host:
The installer needs
systemctl, mount, umount, mkfs.ext4, losetup
and python3. It writes a drop-in for the server unit that:
- sets
CREWSHIP_QUOTA_HELPER_SOCKETandCREWSHIP_QUOTA_HELPER_NAMESPACE(see Environment variables); - orders the server after the helper with
Wants=andAfter=. A helper failure does not stop the server; only quota services with volumes fail to start.
CREWSHIP_CONTAINER_PREFIX and restarts nothing. The
server picks up the drop-in at its next restart.
Recovery and quarantine
On start the helper remounts every volume. It deletes allocations a crash interrupted, and moves malformed catalog entries (metadata without an image, an image without metadata, corrupt or inconsistent metadata) to/var/lib/crewship-quota/INSTANCE/quarantine/ instead of refusing to start.
The result shows in systemctl status crewship-quota-helper@INSTANCE, and each
decision is in the helper’s journal. Quarantined images still count against
--capacity-bytes until you inspect and delete them.
Crew rename and deletion
Volumes are bound to the crew’s id, service, volume name, capacity and generation, not to its slug: renaming a crew keeps its data. Deleting a crew removes its quota volumes. The helper releases and deletes the image first and the Docker volume last, so an interrupted deletion is completed by the next one.Limitations
- Old generations are only released when the crew is deleted (see above).
- Image users given by name are not resolved; such volumes stay root-owned.
- The physical tests (overflow, helper restart, host reboot) run only in dedicated guests with explicit opt-in. No CI workflow runs them yet.
- Backups of quota volumes require the server’s quota helper; without it they are refused.