Skip to main content

LazyFork

Fork running containers with 1 MB of overhead.

A fork is an exact copy of a running container mid-execution: same processes, memory and filesystem, continuing from the same instruction. Forks can be forked again.

How it works:

  • One CRIU dump of the container, many lazy restores from it.
  • A memory page is copied into a fork only when the fork first touches it.
  • Untouched memory is shared through the page cache and costs nothing per fork.
  • The filesystem is an overlay stack; each fork writes to its own layer.
  • No kernel or CRIU patches.

Install

  1. Environment macOS: LazyFork runs its daemon in a Linux VM managed by Lima. Linux: a host with CRIU 4.1+, userfaultfd, overlayfs and cgroup v2, for example Ubuntu 26.04.
  2. LazyFork
 pip install lazyfork

Example

Start a container that counts, look inside it, fork it, and watch the fork continue the count:

lazyfork daemon start
lazyfork create python:3.12-slim --name counter -- \
    python3 -c 'import itertools, time; [print(i, flush=True) or time.sleep(1) for i in itertools.count()]'
lazyfork attach counter          # a shell inside: try `cat /var/log/lazyfork/output.log`, then `exit`
lazyfork fork counter --name twin
lazyfork attach twin            # twin picks up the count where counter was; Ctrl-C to detach

Fork a 200 MB container 100 times

A container holding a 256 MB heap, forked a hundred times:

lazyfork create python:3.12-slim --name big -- \
    python3 -c 'import itertools, time; heap = bytearray(256 << 20); [print(i, flush=True) or time.sleep(1) for i in itertools.count()]'
lazyfork fork big -n 100
lazyfork list                   # big: 260 MB; each fork: under 2 MB; all hundred together: 176 MB
lazyfork status

Only the original image is the full 260MB; forked containers only take ~1.7MB.

id             name   state     parent   depth    pid   procs      memory   image            
 ──────────────────────────────────────────────────────────────────────────────────────────── 
 1bef01acc3f2   big    running   -            0   5424       1    260.6 MB   python:3.12-slim 
 c3e5f821c855   -      running   big          1   7175       1      1.7 MB   python:3.12-slim 
 1fb2162825f9   -      running   big          1   7188       1      1.7 MB   python:3.12-slim 
 a8c319f0ba0c   -      running   big          1   7201       1      1.7 MB   python:3.12-slim 
 dd6c193815e6   -      running   big          1   7214       1      1.7 MB   python:3.12-slim 
 13bf7caf82bb   -      running   big          1   7227       1      1.7 MB   python:3.12-slim 
...

Use

On Linux, run lazyfork daemon run as root. On macOS, the daemon lives in the VM and these commands manage it; everything else runs from the Mac.

lazyfork daemon start           # creates and boots the VM; the first run takes a few minutes
lazyfork daemon status
lazyfork daemon clean           # deletes all stopped containers
lazyfork daemon stop
lazyfork daemon delete          # removes the VM and everything in it

Every container has a generated id, an optional name, and a parent. Commands take a name, an id, or a unique id prefix. lfork is a shorthand for lazyfork.

lazyfork create python:3.12-slim --name agent -- python3 -c 'import time; [time.sleep(1) for _ in iter(int, 1)]'
lazyfork fork agent -n 10           # ten running copies of agent, right now; agent keeps running
lazyfork fork agent --name worker   # a named copy
lazyfork fork worker -n 5           # copies of a copy
lazyfork stop agent                 # snapshot and end the process; still forkable, holds no memory
lazyfork delete worker              # its children keep running
lazyfork exec worker -- hostname    # run a command inside
lazyfork shell worker               # interactive shell inside
lazyfork attach worker              # stream its output
lazyfork freeze worker              # pause; thaw resumes
lazyfork list
lazyfork tree
lazyfork status                     # host, daemon and container totals
lazyfork stats worker               # memory, page faults, restore timing; --csv for one row
from lazyfork.client import Client

agent = Client().container("agent")
with agent.fork(10) as forks:
    outputs = [f.exec(["hostname"])["stdout"] for f in forks]
    grandchildren = forks[0].fork(3)

Optional runtime hook, for runtimes that want a say in when they are forked:

  • Listen on a unix socket inside the container, given with --hook.
  • QUIESCE arrives before a snapshot, RESEED <id> after a restore.
  • lazyfork/hook.py is a drop-in implementation; examples/agent.py uses it.

Benchmark

Setup:

  • python:3.12-slim container running a Python agent with a 128 MB heap.
  • Snapshotted once, forked 1 to 100 times, lazily and with a plain CRIU restore.
  • Each fork runs 5 seconds, touching a few pages per second, then its cgroup memory is read.
  • Lineage rows: forks of forks, nine levels deep.
  • VM: 4 vCPU, 8 GiB, Ubuntu 26.04 arm64. Scripts and CSVs in bench/.
LazyFork plain CRIU restore
restore latency 17 ms 44 ms
memory per fork after restore 1.4 MB 138 MB
100 forks 435 MB 13.8 GB
fork a running fork (snapshot + restore) 170 ms 210 ms
page fault at lineage depth 0 to 8 15 to 25 µs none, all pages copied at restore

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lazyfork-0.1.1.tar.gz (48.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lazyfork-0.1.1-py3-none-any.whl (50.0 kB view details)

Uploaded Python 3

File details

Details for the file lazyfork-0.1.1.tar.gz.

File metadata

  • Download URL: lazyfork-0.1.1.tar.gz
  • Upload date:
  • Size: 48.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.18

File hashes

Hashes for lazyfork-0.1.1.tar.gz
Algorithm Hash digest
SHA256 bba4bfbb3dbacd2d7aab4b3182acdacc49db7f92c88672339d9224bc5712f895
MD5 d075ea23565b86ca214caa63d3ce5bfc
BLAKE2b-256 198f4939ded8440adf43f30171be174e680ada5928211561b57d01f83c2b4a22

See more details on using hashes here.

File details

Details for the file lazyfork-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: lazyfork-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 50.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.18

File hashes

Hashes for lazyfork-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9d8a051a33f809166a93b957ff4024bc57272f991bb5eed6f286f0c899c6e3ee
MD5 eb843c8e01a17c71ba6669c60eefc7ed
BLAKE2b-256 951f114e2e9f94e67feb77a74df5b7a1e99fb0265f5e50fb922a0becc7acd37a

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.1

2 files

This release

0.1.1 This release

2 files

0.1.0

2 files

0.0.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page