Skip to main content

Container Lifecycle

Graft manages the container lifecycle by recreating it with a host bind mount pointing at the current branch directory.

Bind Mounts

A Docker bind mount maps a host directory directly into the container:
/Users/alice/.graft/my-postgres/branches/experiment:/var/lib/postgresql/data
On macOS, this goes through the virtio-fs filesystem sharing protocol (Docker Desktop 4.22+ and OrbStack). The database engine sees its data directory at the expected path and runs normally.

Smart Lifecycle

Graft always captures whether the container was running before any lifecycle operation:
wasRunning, _ := client.IsRunning(ctx, s.TargetContainer)
If the container was already stopped:
  • Stop is skipped
  • Start is skipped
  • Only the recreate happens
If the container was running:
  • Graceful stop (SIGTERM)
  • Recreate with new bind mount
  • Start
This means running graft checkout on an already-stopped container is faster — only the filesystem work and container recreation happen.

Recreate Flow

1. Inspect container → capture wasRunning state
2. Stop container (if running)
3. Remove container
4. Create new container with:
   - Same image
   - Same environment variables
   - Same port bindings
   - Same network mode
   - NEW: bind mount → branch directory
5. Start container (if was running)

Container Metadata Fidelity

When Graft recreates a container, it faithfully reproduces the original configuration:
SettingSourcePreserved
ImageContainer inspectYes
Environment variablesContainer inspectYes
Port bindingsContainer inspectYes (host port may change)
Network modeContainer inspectYes
Exposed portsContainer inspectYes
Container nameSame as inputYes
LabelsNot yet preservedOn roadmap
The container’s identity (name) stays the same, so Docker Compose and other tools that reference the container by name continue to work.

Why Stop the Container?

Database engines cannot be safely snapshotted while running. Postgres uses a Write-Ahead Log (WAL) for durability. Copying data while Postgres is writing would produce:
  • Half-written WAL segments
  • Incomplete pg_wal/ directory
  • Corrupted heap files
Graft’s downtime is measured in seconds, not minutes. The container is stopped only during the filesystem operation, then immediately restarted.

Performance Impact

Bind Mount Performance (macOS)

EnvironmentThroughputNotes
Linux native~1000-3000 MB/sNo VM layer
OrbStack~400-1000 MB/sOptimized virtio-fs
Docker Desktop 4.22+~200-500 MB/svirtio-fs
Docker Desktop (older)~50-150 MB/sLegacy osxfs
For development workloads, even the slowest environment is acceptable. Production-grade throughput is not a goal — Graft is a development tool.

Snapshot Mode (No Container Management)

Without --bind, Graft leaves the container on its original Docker volume. At commit time, an ephemeral Alpine runner copies data from the Docker volume to the host. The container is never recreated. This mode has higher commit latency (the Alpine runner must spin up and copy data) but zero container lifecycle risk.