Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,32 @@ git push origin feature-b --force

**One PR per concern:** Unrelated changes get separate PRs.

### Claude Review Workflow

PRs trigger an automated Claude review via GitHub Actions. After pushing:

```bash
# Wait for review check to complete
gh pr checks <pr-number>
# Look for: review pass 4m13s ...

# Read review comments
gh pr view <pr-number> --json comments --jq '.comments[] | .body'
```

If review finds critical issues, it may auto-create a fix PR. Cherry-pick the fix:
```bash
git fetch origin
git cherry-pick <fix-commit>
git push
gh pr close <fix-pr-number> # Close the auto-generated PR
```

**MANDATORY before merging any PR:** Read all review comments first:
```bash
gh pr view <pr-number> --json comments --jq '.comments[] | .body'
```

### PR Descriptions: Show, Don't Tell

**CRITICAL: Review commits in THIS branch before writing PR description.**
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ jobs:
sudo sysctl -w vm.unprivileged_userfaultfd=1
# Disable AppArmor restriction on unprivileged user namespaces (needed for rootless networking on newer Ubuntu)
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 2>/dev/null || true
# Enable IP forwarding for all interfaces (including future ones like podman0)
# This fixes podman container networking - without this, containers can't reach the internet
sudo sysctl -w net.ipv4.conf.all.forwarding=1
sudo sysctl -w net.ipv4.conf.default.forwarding=1
# Enable FUSE allow_other for tests
echo "user_allow_other" | sudo tee /etc/fuse.conf
# Reset podman state if corrupted from previous runs
Expand Down Expand Up @@ -366,6 +370,10 @@ jobs:
sudo sysctl -w vm.unprivileged_userfaultfd=1
# Disable AppArmor restriction on unprivileged user namespaces (needed for rootless networking on newer Ubuntu)
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 2>/dev/null || true
# Enable IP forwarding for all interfaces (including future ones like podman0)
# This fixes podman container networking - without this, containers can't reach the internet
sudo sysctl -w net.ipv4.conf.all.forwarding=1
sudo sysctl -w net.ipv4.conf.default.forwarding=1
echo "user_allow_other" | sudo tee /etc/fuse.conf
podman system migrate || true
# Install iperf3 for network benchmarks
Expand Down Expand Up @@ -459,6 +467,10 @@ jobs:
sudo sysctl -w vm.unprivileged_userfaultfd=1
# Disable AppArmor restriction on unprivileged user namespaces (needed for rootless networking on newer Ubuntu)
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 2>/dev/null || true
# Enable IP forwarding for all interfaces (including future ones like podman0)
# This fixes podman container networking - without this, containers can't reach the internet
sudo sysctl -w net.ipv4.conf.all.forwarding=1
sudo sysctl -w net.ipv4.conf.default.forwarding=1
# Configure rootless podman to use cgroupfs (no systemd session on CI)
mkdir -p ~/.config/containers
printf '[engine]\ncgroup_manager = "cgroupfs"\nevents_logger = "file"\n' > ~/.config/containers/containers.conf
Expand Down
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ echo "user_allow_other" | sudo tee -a /etc/fuse.conf
# Ubuntu 24.04+: allow unprivileged user namespaces
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

# IP forwarding for container networking (e.g., podman builds)
sudo sysctl -w net.ipv4.conf.all.forwarding=1
sudo sysctl -w net.ipv4.conf.default.forwarding=1

# Bridged networking only (not needed for --network rootless):
sudo mkdir -p /var/run/netns
sudo iptables -P FORWARD ACCEPT
Expand Down Expand Up @@ -176,6 +180,37 @@ fcvm automatically caches container images after the first pull. On subsequent r

The snapshot captures VM state **after image pull but before container start**. On restore, fc-agent runs `podman run` with the already-pulled image, skipping the slow pull/export step.

### Two-Tier Snapshot System

fcvm uses a two-tier snapshot system for optimal startup performance:

| Snapshot | When Created | Content | Size |
|----------|--------------|---------|------|
| **Pre-start** | After image pull, before container runs | VM with image loaded | Full (~2GB) |
| **Startup** | After HTTP health check passes | VM with container fully initialized | Diff (~50MB) |

**How diff snapshots work:**
1. **First snapshot (pre-start)**: Creates a full memory snapshot (~2GB)
2. **Subsequent snapshots (startup)**: Copies parent's memory.bin via reflink (CoW, instant), creates diff with only changed pages, merges diff onto base
3. **Result**: Each snapshot ends up with a **complete memory.bin** - equivalent to a full snapshot, but created much faster

**Key insight**: We use reflink copy + diff merge, not persistent diff chains. The reflink copy is instant (btrfs CoW), and the diff contains only ~2% of pages (those changed during container startup). After merging, you have a complete memory.bin that can be restored without any dependency on parent snapshots.

The startup snapshot is triggered by `--health-check <url>`. When the health check passes, fcvm creates a diff snapshot of the fully-initialized application. Second run restores from the startup snapshot, skipping container initialization entirely.

```bash
# First run: Creates pre-start (full) + startup (diff, merged)
./fcvm podman run --name web --health-check http://localhost/ nginx:alpine
# → Pre-start snapshot: 2048MB (full)
# → Startup snapshot: ~50MB (diff) → merged onto base

# Second run: Restores from startup snapshot (~100ms faster)
./fcvm podman run --name web2 --health-check http://localhost/ nginx:alpine
# → Restored from startup snapshot (application already running)
```

**Parent lineage**: User snapshots from clones automatically use their source snapshot as a parent, enabling diff-based optimization across the entire snapshot chain.

### More Options

```bash
Expand Down
Loading