12. Best Practices
Hint
Abstract
This chapter collects recommended setups for common use cases. Each section combines the registration and configuration steps of the previous chapters into one complete setup.
12.1. AI Workloads
AI coding tools read and write the data you work on, run commands on their own and talk to the network. A Firecracker VM application keeps such a tool on a kernel of its own, separated from the file system of the host. The tool reaches only what you connect it to: the network it needs to talk to its model provider, and one directory of the host which holds the data you allow it to see.
The following setup registers an AI tool from one of the pre-built images, connects it to the network, shares one directory of the host with it and logs it in with an API key. All examples register the application for the calling user and expect the setup described in Getting Started.
12.1.1. The Pre-Built Images
Pre-built images are available for the following tools. Every image
comes with a user named ai, home directory /home/ai, which is
the user to run the tool as:
Image |
Provides |
API key login |
|---|---|---|
|
Claude Code |
|
|
GitHub Copilot CLI and the GitHub CLI |
|
|
The |
|
|
The |
The variable of the provider, e.g |
The images and their versions are listed at
https://ddrasqgvrmpt8.cloudfront.net/. An image archive is named
NAME.ARCH-VERSION-RELEASE.tar.xz and is pulled from that location,
e.g https://ddrasqgvrmpt8.cloudfront.net/claude.x86_64-1.16.0-0.tar.xz.
Check the listing for the current version of the image you want to
use. The examples below use the claude image; for another image
replace the image name, the application name and the archive name.
Note
The pre-built images are based on openSUSE Leap, but Firecracker images can be built for any distribution, e.g Fedora. The base images in https://github.com/OSInside/flake-pilot/tree/main/appstore/firecracker/leap and https://github.com/OSInside/flake-pilot/tree/main/appstore/firecracker/fedora show the setup for both distributions, How To Build Your Own App Images explains how to build an image. Images for other distributions can be added on request, open an issue at https://github.com/OSInside/flake-pilot/issues.
12.1.2. Pull the Image
flake-ctl firecracker pull --name claude \
--kis-image https://ddrasqgvrmpt8.cloudfront.net/claude.x86_64-1.16.0-0.tar.xz
The image is stored in the local firecracker registry under the name
claude. Pulling it again later fetches a new version only if the
checksum of the archive changed, see Applications From a Virtual Machine.
12.1.3. Register the Application
flake-ctl firecracker register --vm claude \
--app $HOME/bin/claude --target /bin/bash --user ai \
--overlay-size 50GiB --force-vsock --resume
--user aiRuns the application as the
aiuser of the image instead ofroot. The tool works in the home directory of that user and keeps its settings there. If the user does not exist in the VM the application is called asroot.--target /bin/bashCalling
claudeon the host drops you into a shell of theaiuser inside of the VM. From there you set up the login and start the tool, see below.--overlay-size 50GiBThe storage of the instance. Everything written inside of the VM, e.g the settings of the tool, packages it installs, caches and the data it creates outside of the shared directory, is stored there. The storage is created on the first start, kept after the instance is gone and reused on its next start. It is stored below
~/.config/flakes/firecracker/storageon the host.--force-vsockand--resumeThe instance keeps running after the call and further calls of
claudeare executed inside of it, which keeps the running sessions of the tool available. A resumed instance talks to the host through a vsock connection instead of the serial console.
12.1.4. Connect the Network
The tool needs the network to reach its model provider:
flake-ctl firecracker network init
flake-ctl firecracker network add --app $HOME/bin/claude
init prepares the host and is needed once per host, add
connects the application. Both commands change the network
configuration of the host through the Flake Pilot helper, which
requires a sudo rule, see Privileges and Trust. After a reboot of the
host both have to be called again, the application keeps its address.
A connected VM reaches everything the host reaches through its outgoing interface. On a cloud instance, keep it away from the metadata service of the cloud, and if the tool needs no access to your local network, from the private networks as well:
flake-ctl firecracker network init \
--egress-deny link-local --egress-deny private
If the tool only needs a known set of services, e.g the API of its model provider, allow only those. Everything else, including services the tool contacts without being asked, is dropped then:
flake-ctl firecracker network init \
--egress-deny link-local --egress-deny private \
--egress-allow api.example.com
Firecracker Networking explains the setup and the policy.
12.1.6. Log in With an API Key
Call the application and log the tool in with the API key of your provider from the shell inside of the VM:
claude
claudeexport ANTHROPIC_API_KEY=YOUR_API_KEY claude
Claude Code asks once to approve the key on its first start.
copilotexport COPILOT_GITHUB_TOKEN=YOUR_TOKEN copilot
The token is a fine-grained personal access token of your GitHub account with the Copilot Requests permission. Classic personal access tokens are not supported by the Copilot CLI. The GitHub CLI
ghreads its token fromGH_TOKEN.deepseekexport DEEPSEEK_API_KEY=YOUR_API_KEY pi
Select one of the DeepSeek models with
/model.piexport ANTHROPIC_API_KEY=YOUR_API_KEY pi
Use the variable of your provider, e.g
OPENAI_API_KEY,GEMINI_API_KEYorOPENROUTER_API_KEY, and select a model of that provider with/model.
An exported key is known to the shell it is exported in. To keep it
for the next start of the instance, add the export line to
/home/ai/.bashrc inside of the VM. The file is stored in the
storage of the instance, never put a key into the shared directory.
An API key is the simplest way to log in, but not the only one. The
tools also support the login to a subscription of the provider or
the access to the models through a cloud platform, e.g Google Vertex
AI or Amazon Bedrock. Most of these methods require an extra utility
in the VM which handles the login to that platform, e.g gcloud
for Google Vertex AI. The claude image comes with gcloud,
Claude Code uses Google Vertex AI as follows:
gcloud auth application-default login
export CLAUDE_CODE_USE_VERTEX=1
export CLOUD_ML_REGION=YOUR_REGION
export ANTHROPIC_VERTEX_PROJECT_ID=YOUR_PROJECT_ID
claude
A utility which is not part of the image can be installed, see below. The documentation of the tool lists the methods it supports.
12.1.7. Working With AI Instances
Because of --resume the instance keeps running after the first
call. Every further call of claude on the host, e.g from another
terminal, opens one more shell of the ai user in the same
instance. All of these sessions share the file system, the processes
and the login of the instance: run the tool in one session, and look
at the files it changes, run the tests of your project or install
software from another one.
An agent might need software which is not part of the pre-built
image, e.g a compiler or the build tools of your project. The images
are based on openSUSE Leap, software is installed with zypper as
root. The ai user is allowed to run any command as root
through sudo without a password, the rule is part of the images
in /etc/sudoers.d/ai:
sudo zypper install gcc make
The software is stored in the storage of the instance and kept for its next start.
With the rule the tool can install what it needs by itself, and it
can run any other command as root inside of the VM without asking
you as well. root of the VM reaches nothing of the host beyond
what the ai user reaches: the shared directory is accessed with
the rights of its owner on the host either way, and the network is
the same. If you want to keep root to yourself, set a root
password of your own and remove the rule afterwards:
sudo passwd root
sudo rm /etc/sudoers.d/ai
From then on you become root with su - and your password, the
change is kept in the storage of the instance as well.
Calling the application with an @NAME argument starts a separate
instance, which runs in a firecracker process of its own:
claude @project-a
The argument selects the instance and is not passed on to the shell.
A name consists of letters, digits, _, - and =. Every
instance has a storage of its own, e.g claude@project-a.ext4
below ~/.config/flakes/firecracker/storage, and is resumed on its
own: further calls of claude @project-a open more sessions in
that instance, calls without the argument in the instance of the
application. Use separate instances for AI workloads which must not
see the data of each other, e.g one instance per project or per
customer.
An instance needs a network setup of its own, a TAP device cannot be shared across instances. To keep the data of the instances apart, give each of them a shared directory of its own as well, which takes the place of the directory of the application:
flake-ctl firecracker network add \
--app $HOME/bin/claude --instance @project-a
mkdir -p $HOME/ai-share-project-a
flake-ctl firecracker volume export \
--path $HOME/ai-share-project-a \
--app $HOME/bin/claude --instance @project-a
flake-ctl firecracker volume add \
--app $HOME/bin/claude --instance @project-a \
--volume $HOME/ai-share-project-a:/home/ai/share
Like the application, the instance has to be connected again after a
reboot of the host. A new instance starts from the plain image: log
in, and if needed install software or remove the sudo rule, in
that instance again. Every running instance is a VM of its own and takes
its share of the memory and the processors of the host.
12.1.8. The Instance Is Disposable
The instance is not a valuable system. It is the runtime of an AI workload, the valuable part is the data the workload produces. Keep that data in the shared directory, which is stored on the host, or e.g commit and push it to a repository, and treat the instance as something you can throw away. This means you can kill an instance at any time, e.g when a tool does something you did not ask for, to make sure no AI keeps running wild.
flake-ctl firecracker show lists the instances along with the
process ID of their firecracker process in the id column and
their status:
flake-ctl firecracker show
Killing that process stops the VM at once, along with the tool and all sessions of the instance:
kill PROCESS_ID
The next call of the application starts the instance again from its storage. The storage of an instance commits its journal every second. If the instance gets killed, only the data written to the storage in the last second before can be lost, and the journal keeps the file system of the storage consistent. A file in the shared directory which the tool was writing at that moment may be incomplete.
12.1.9. What Remains Reachable
With this setup the tool sees the file system of the VM, the shared directory and the network the host reaches through its outgoing interface. It does not see the file system of the host otherwise. Isolation and Its Limits summarizes what a connected VM can reach.