How I Used Lima for an AI Coding Agent Sandbox

My experience with AI coding agents, like Claude Code, Codex CLI, or Augment Code’s Auggie has been that they are most effective when they can run autonomously, without frequent human intervention. In order to do that the AI needs permissions to make changes to the codebase, run tests, perform web searches, etc.

Each AI coding agent CLI has its own approach to permissions and sandboxing. Maybe at some point I’ll feel comfortable tweaking those permissions and allowing an agent to run freely on my development laptop directory. But I’m not there yet, so I’ve been using Lima to run a VM that provides a sandboxed environment where the agent has access to my source code, but nothing else on my laptop.

Lima

From the Lima (Linux Machines) website:

Lima launches Linux virtual machines with automatic file sharing and port forwarding (similar to WSL2).

The file sharing and port forwarding make it a perfect choice for creating a sandboxed environment for an AI coding agent. The VM can be configured to have access to a specific directory (or directories) on the host machine, and you can easily access a dev server, or any other service, running in the VM thanks to the automatic port forwarding.

Installation is simple:


  brew install lima

VM Configuration

The Lima documentation has a good overview of how to get started (creating, running, etc.) with a default VM. But for my purposes I wanted to create a specific VM that I’d use for AI coding agents, so I created a custom configuration file.

The default behavior in Lima is to mount your entire home directory. This is very convenient, but I don’t want an agent to have access to anything outside of the project directory. I also wanted to make it easy to re-create the VM if needed, so I made sure the configuration file had everything in it I’d need for my current project.

My project uses mise to manage tooling / dependencies, so I’m using it to also install the AI coding agents in the VM.

For some of the initial environment and tooling setup I found that it works better to provision a script that you can manually run after the VM is created. My first attempt had the tools/dependencies installing as part of the VM provisioning, but if things took a while (apt update/install, for example) there was no feedback and it would occasionally time out.

Here’s the configuration file (I named it sandbox.yaml):


base: template:ubuntu-lts

vmType: "vz"

# The following disables rosetta, mostly for further sandbox isolation
# But if you need rosetta (to run x86_64 binaries, you can comment this out)
vmOpts:
  vz:
    rosetta:
      enabled: false
      binfmt: false

# Adjust based on your dev machine and space needs
cpus: 10
disk: "15GiB"

# We'll specify a mount when creating the VM
mounts: []

provision:
  - mode: data
    path: /home/{{.User}}.linux/install.sh
    owner: "{{.User}}"
    permissions: 755
    content: |
      #!/bin/bash
      set -eux -o pipefail

      echo "Installing development environment..."
      echo "This may take several minutes..."

      # Update and install packages (your needs may vary)
      sudo apt update && sudo apt install -y \
        libatomic1 zip net-tools build-essential \
        zlib1g-dev libssl-dev  libreadline-dev \
        libyaml-dev libffi-dev libgmp-dev

      # Install mise
      echo "Installing mise..."
      curl https://mise.run | sh

      # Add mise to PATH for this script
      export PATH="$HOME/.local/bin:$PATH"

      # Configure mise activation in .bashrc (for interactive shells)
      if ! grep -q 'mise activate' ~/.bashrc; then
        echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc
      fi

      # Configure mise activation in .profile (for login/non-interactive shells)
      if ! grep -q 'mise activate' ~/.profile; then
        echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.profile
      fi

      # Configure vim as the editor
      echo 'export VISUAL=vim' >> ~/.bashrc
      echo 'export EDITOR=vim' >> ~/.bashrc
      echo 'export VISUAL=vim' >> ~/.profile
      echo 'export EDITOR=vim' >> ~/.profile

      # Install node first (required for AI agent npm packages)
      echo "Installing Node.js (this may take a while)..."
      mise use -g node@latest

      # Install npm packages
      echo "Installing npm packages..."
      mise use -g npm:@augmentcode/auggie@latest
      mise use -g npm:@openai/codex@latest
      mise use -g npm:@anthropic-ai/claude-code@latest

      # Configure project specific environment variables
      echo "Configuring mise environment variables..."
      mkdir -p ~/.config/mise
      cat > ~/.config/mise/mise.toml << 'EOF'
      [env]
      ConnectionStrings__TestDb = "Server=host.lima.internal,1434;Database=MyTestDb;Integrated Security=false;MultipleActiveResultSets=true;App=EntityFramework;User Id=sa;Password=ThePassword;Encrypt=false;"
      AcceptanceTest__DatabaseServerName = "host.lima.internal,1434"
      EOF

      # Install project dependencies
      mise trust
      mise install

      echo ""
      echo "Installation complete!"

Mise has a direnv style capability to set environment variables when you’re working in a directory. I’m taking advantage of that to override some environment variables so the application/tests can connect to the database running on the host machine. host.lima.internal is the hostname of the host machine as seen from within the VM.

Creating and Running the VM

With the above configuration file in place, creating the VM is a matter of creating the VM from your project directory (wherever that is on your host machine):


limactl create --name=sandbox --set ".mounts=[{\"location\": \"$PWD\", \"writable\": true, \"mountPoint\": \"/mnt/sandbox\"}]" ~/Downloads/sandbox.yaml

Next, start the VM:


limactl start sandbox

For convenience, I’ve added sandbox alias to my .zshrc that will open the shell and change the working directory to the mounted project directory:


cat >> ~/.zshrc << 'EOL'
alias sandbox='limactl shell --workdir /mnt/sandbox sandbox'
EOL
source ~/.zshrc

The last step is to run the install script:


sandbox bash -c '~/install.sh'

Run Commands in the VM

Once the VM has been started, you can run any project command from with the VM by starting your command with sandbox.


sandbox dotnet test

sandbox codex --dangerously-bypass-approvals-and-sandbox

sandbox claude --dangerously-skip-permissions

sandbox auggie

Multiple Projects

The VM as it stands now has a single mount point for the project directory. If you’d like to use this same VM for multiple projects, you can stop the VM (limactl stop sandbox) and edit the configuration file – it will be located at ~/.lima/sandbox/lima.yaml. You can just duplicate the one mount entry to add additional mount points, and then add new aliases to your .zshrc that use the appropriate working directory.

Or just create a separate VM for each project, starting and stopping them as needed (for better isolation).

Conclusion

For me, letting an AI coding agent run freely in a sandboxed environment has been a very effective way to take advantage of the power of these tools. I highly recommend giving it a try.

Conversation

Join the conversation

Your email address will not be published. Required fields are marked *