)\ /(
( \/ ) ╦╔╦╗╔═╗
( ● ● )~~✦ ║║║║╠═╝
\ ‿ / ╩╩ ╩╚
( )~,
/\ /\ v0.1.0 · imp.dev
Imp is a Kubernetes operator and node agent for running Firecracker microVM workloads as first-class Kubernetes resources.
In plain terms: it gives you lightweight mini-VMs that behave like disposable app sandboxes, so you can run risky or isolated workloads without giving them access to your whole host.
A microVM is a very small virtual machine with stronger isolation than a container, and Firecracker is the open-source microVM runtime Imp uses to start those sandboxes quickly.
It provides CRDs for VM lifecycle, VM networking, snapshots, migrations, warm pools, and runner pools, with Cilium-first networking support, VXLAN fallback for non-Cilium CNIs, and built-in metrics for VM state, latency, and health.
ImpVM: microVM lifecycle and schedulingImpNetwork: VM network, NAT, DNS, and optional Cilium integrationImpNetworkAttachment: RBAC-gated physical LAN/VLAN attachmentImpVMSnapshot: VM state snapshot lifecycleImpVMMigration: migration orchestrationImpWarmPool: prewarmed VMs from snapshotsImpVMRunnerPool: VM pools for CI-style runner workloadsImpVMClass,ImpVMTemplate,ClusterImpConfig,ClusterImpNodeProfile
Excalidraw source: docs/diagrams/imp-architecture.excalidraw
- GPU passthrough is not supported. Firecracker in this project is used for CPU/memory/storage-isolated microVM workloads only.
- Go
1.25.6+ - Docker or compatible container runtime
kubectl- A Kubernetes cluster (Kind is supported for e2e)
helm(recommended install path)- At least one Ready, schedulable node labeled
imp/enabled=true. This label is the explicit opt-in for both the Imp scheduler and the privileged node agent. Manage it through your cluster's node configuration source of truth (for Talos/Omni, a machine configuration patch), not through Helm.
Imp's node agent mounts /dev/kvm and narrow host paths for Firecracker and
the guest kernel. It therefore requires a dedicated privileged namespace. Do
not relax Pod Security Admission for application namespaces or the whole
cluster.
kubectl create namespace imp-system --dry-run=client -o yaml | kubectl apply -f -
kubectl label namespace imp-system pod-security.kubernetes.io/enforce=privileged --overwrite
kubectl get nodes -l imp/enabled=true
helm upgrade --install imp ./charts/imp -n imp-system --create-namespace
kubectl -n imp-system get podsThe chart defaults agent.nodeSelector and kvm.preflight.nodeSelector to
imp/enabled=true. Keep that required selector when adding placement
constraints.
Treat each node running the Imp agent as part of the microVM control-plane trust boundary. The agent is privileged to access KVM; this is not a cluster-wide workload profile.
- Run agents and ImpVM workloads on a dedicated node pool. Protect its label
with the
node-restriction.kubernetes.io/prefix, taint the nodes withNoSchedule, and configure the chart'sagent.nodeSelectorandagent.tolerationsaccordingly. Do not place general application workloads on that pool. - Keep
imp-systemas the only namespace with theprivilegedPod Security profile. Keep all other namespaces atrestrictedunless they have an independently justified exception. - Pin operator and agent images by digest in production values. Pin and verify the Firecracker, Jailer, guest-kernel, and rootfs artifact provenance too.
- Keep host access narrow:
/dev/kvm, read-only Firecracker and guest-kernel files, and the dedicated Imp socket directory only. Do not mount the host root filesystem, container-runtime sockets, or broad host directories. - Use Firecracker's Jailer for every VM with a unique jail root and socket, unprivileged Firecracker UID/GID, cgroup limits, and Firecracker's default seccomp filters. Never disable Firecracker seccomp.
- Apply least-privilege RBAC and default-deny NetworkPolicies, allowing only the API, DNS, operator, and metrics paths each component requires.
privileged overrides important Linux security constraints, so SELinux,
AppArmor, pod seccomp, and RuntimeClass do not replace node-pool isolation for
the agent. Reducing the agent to a tested capability set is tracked work; do
not guess that set in production.
make install
make deploy IMG=<registry>/imp-operator:<tag>helm uninstall imp -n imp-system
# or (kustomize path)
make undeploy
make uninstallApply a minimal network and VM in default namespace:
kubectl apply -f - <<'EOF'
apiVersion: imp.dev/v1alpha1
kind: ImpNetwork
metadata:
name: quick-net
namespace: default
spec:
subnet: 192.168.100.0/24
nat:
enabled: true
---
apiVersion: imp.dev/v1alpha1
kind: ImpVM
metadata:
name: quick-vm
namespace: default
spec:
image: docker.io/library/nginx:1.27-alpine
networkRef:
name: quick-net
EOFCheck status:
kubectl get impvm -n default
kubectl describe impvm quick-vm -n default
kubectl get impnetwork quick-net -n default -o yamlImp can automatically delete a microVM after a fixed runtime window.
0or unset means disabled- minimum enabled value is
60s - expiration is anchored to first
status.runningAt - on expiry, the controller issues normal
Delete(graceful stop path first)
Resolution precedence:
ImpVM.spec.expireAfter- creator pool (
ImpVMRunnerPool.spec.expireAfter/ImpWarmPool.spec.expireAfter) ImpVMTemplate.spec.expireAfter- disabled
Quick example:
apiVersion: imp.dev/v1alpha1
kind: ImpVMTemplate
metadata:
name: ci-runner-template
namespace: default
spec:
classRef:
name: ci-small
image: ghcr.io/syscode-labs/test:latest
expireAfter: 2h
---
apiVersion: imp.dev/v1alpha1
kind: ImpVMRunnerPool
metadata:
name: ci-runner-pool
namespace: default
spec:
templateName: ci-runner-template
expireAfter: 45m
platform:
type: github-actions
scope:
repo: your-org/your-repo
credentialsSecret: gh-runner-tokenSee examples/runner-pool-expiration for a complete runnable teaser.
When using ImpVMRunnerPool with GitHub Actions, GitHub does not read pool objects directly.
It only sees registered self-hosted runner instances.
Read: github-capacity-signaling.md
ImpVMRunnerPool supports explicit mode-driven scaling through spec.scaling for GitHub Actions (platform.type=github-actions).
Required explicit fields:
mode(webhook,polling,hybrid)minIdlemaxConcurrentscaleUpStepcooldownSeconds
This avoids hidden capacity assumptions and makes scaling intent explicit.
Example:
make test
make lint
make buildRun operator locally:
make runRun e2e tests (isolated Kind cluster):
make test-e2eThe repository includes IaC scripts under hack/:
hack/oci-build-golden-image.shhack/packer-build-golden-image.sh(default build driver: native Packer OCI builder with script preflight/sanitization)hack/oci-firecracker-e2e.sh
hack/oci-build-golden-image.sh is idempotent for missing OCI inputs:
- auto-detects compartment and AD for
VM.Standard.E2.1.Micro - reuses existing public subnet or creates a minimal public VCN/subnet stack
- prunes oldest
imp-fc-golden-*images when custom-image quota is full
hack/packer-build-golden-image.sh (recommended) now also performs post-build retention cleanup of old prefixed custom images:
OCI_POST_BUILD_PRUNE_OLD_IMAGES(defaulttrue)OCI_POST_BUILD_KEEP_IMAGES(default1)
Build a minimal golden image:
IMP_OCI_PROFILE=syscode-api \
IMP_OCI_COMPARTMENT_NAME=homelab \
IMP_OCI_DOMAIN_NAME=homelab \
OCI_SSH_PUBLIC_KEY_FILE="$HOME/.ssh/builder.pub" \
OCI_SSH_PRIVATE_KEY_FILE="$HOME/.ssh/builder" \
OCI_OUTPUT_ENV_FILE="$HOME/.config/imp/oci-golden.env" \
hack/oci-build-golden-image.shBuild via native Packer OCI builder:
IMP_OCI_PROFILE=syscode-api \
IMP_OCI_COMPARTMENT_NAME=homelab \
IMP_OCI_DOMAIN_NAME=homelab \
OCI_OUTPUT_ENV_FILE="$HOME/.config/imp/oci-golden.env" \
hack/packer-build-golden-image.shRun e2e using a generated image:
source "$HOME/.config/imp/oci-golden.env"
IMP_OCI_PROFILE=syscode-api \
IMP_OCI_COMPARTMENT_NAME=homelab \
IMP_OCI_DOMAIN_NAME=homelab \
OCI_GOLDEN_BUILD_DRIVER=packer \
OCI_SSH_PUBLIC_KEY_FILE="$HOME/.ssh/builder.pub" \
OCI_SSH_PRIVATE_KEY_FILE="$HOME/.ssh/builder" \
OCI_IMAGE_OCID="$OCI_IMAGE_OCID" \
hack/oci-firecracker-e2e.shNotes:
- OCI boot volume minimum is
50GB. - Golden image max size is controlled by
OCI_GOLDEN_MAX_GB(default50GiB). - Optional
OCI_GOLDEN_ZERO_FILL=truecan reduce sparse image footprint but is slower. - For automation, use an unencrypted SSH key or load passphrase keys in
ssh-agent. - e2e auto-build defaults to
OCI_GOLDEN_BUILD_DRIVER=packer; setnative-ocionly if needed.
Imp provides first-class integration with Cilium. When Cilium is detected:
- VMs are enrolled as
CiliumExternalWorkloadresources - Kubernetes
NetworkPolicyapplies to VMs - VM traffic is visible in Hubble
- VMs can reach
ClusterIPservices via kube-dns
For non-Cilium CNIs (Flannel/Calico/Weave/etc.), Imp uses a VXLAN fallback for cross-node VM connectivity.
Cilium IPAM runbook: docs/networking/cilium-ipam.md
Default isolated networking and the elevated LAN/VLAN attachment model:
docs/networking/privileged-lan-attachment.md
Need a VM on a physical, administrator-allowlisted VLAN (tagged or untagged)?
ImpNetworkAttachment provides RBAC-gated access-mode attachment with optional
guest DHCP — see examples/lan-vlan-attachment/.
Imp exposes operator and agent metrics so you can monitor VM lifecycle and platform health:
- VM state and phase metrics
- Scheduling/boot latency metrics
- Guest resource metrics (CPU, memory, disk)
- Prometheus-compatible scraping and dashboards
sequenceDiagram
participant U as User
participant APIS as K8s API
participant OP as Imp Operator
participant AG as Imp Agent (Node)
participant FC as Firecracker
U->>APIS: Create ImpVM + ImpNetwork
APIS->>OP: Watch event
OP->>OP: Resolve class/template + schedule node
OP->>AG: Desired VM runtime spec
AG->>FC: Create machine + start microVM
FC-->>AG: Runtime state, PID, network info
AG-->>APIS: Status updates
OP-->>APIS: Conditions / phase transitions
ImpVM stuck in Pending: check scheduler events and node capacity.kubectl describe impvm <name> -n <ns>- No VM IP / networking issues: inspect
ImpNetworkstatus and agent logs.kubectl -n imp-system logs ds/imp-agent - Cilium features not active: verify Cilium CRDs exist and cni detection events.
kubectl get crd | grep cilium - Cilium IPAM/pool issues: verify
CiliumPodIPPoolexists andImpNetwork.spec.ipam.cilium.poolRefmatches.kubectl get ciliumpodippool - Webhook admission failures: check cert-manager/webhook pods and certificates.
kubectl -n imp-system get pods,certificates,issuers - Snapshot or migration stalls: inspect related CR conditions.
kubectl describe impvmsnapshot <name> -n <ns>kubectl describe impvmmigration <name> -n <ns> - OCI image/e2e script failures: validate profile/session and compartment/env file values.
oci session validate --profile syscode-apisource ~/.config/imp/oci-golden.env
- Control plane:
- Operator runs with Kubernetes RBAC scoped to Imp CRDs and required core resources.
- Admission webhooks validate critical resources (
ImpVM,ImpVMClass,ImpVMTemplate).
- Node plane:
- Agent is privileged to manage Firecracker, networking, and host paths.
- Limit agent deployment to trusted nodes via taints/selectors if required.
- OCI automation:
- Use dedicated API user/group (
homelab-api) with least-privilege policy. - Current policy is scoped to
syscode-labs:homelabfor compute/network/volume families. - Avoid tenancy-wide
manage all-resources; use temporary broad grants only for debugging.
- Use dedicated API user/group (
- Supply chain:
- Pin operator/agent image tags in Helm values for reproducible deployments.
- Prefer private registries and signed images where possible.
- Secrets:
- Keep OCI keys out of repo and use local profile files or external secret stores.
- Use short-lived session tokens for admin profiles where practical.
Create a single install bundle:
make build-installer IMG=<registry>/imp-operator:<tag>This generates dist/install.yaml.
Contributions are welcome. Before opening a PR:
make manifests generate
make lint-fix
make testCopyright 2026.
Licensed under the Apache License, Version 2.0.
