Deploy Bouvet on your own infrastructure for secure, isolated code execution sandboxes.
Deploy to AWS in one command:
cd terraform && terraform init && terraform apply -var="ssh_key_name=YOUR_KEY_NAME"That's it. Wait ~5 minutes, then grab your endpoint:
terraform output mcp_endpointFor AWS Graviton instances (better price/performance):
terraform apply \
-var="ssh_key_name=YOUR_KEY_NAME" \
-var="instance_type=a1.metal" \
-var="architecture=arm64"| Variable | Description | Default |
|---|---|---|
ssh_key_name |
Required. AWS EC2 key pair name | — |
aws_region |
AWS region | us-east-1 |
availability_zone |
Availability zone | us-east-1a |
instance_type |
EC2 instance type (must be .metal) |
c5.metal |
architecture |
Target architecture (x86_64/arm64) |
x86_64 |
docker_image |
Docker image for bouvet-mcp | ghcr.io/vrn21/bouvet-mcp:latest |
rootfs_url |
Public URL for rootfs download | Architecture-specific S3 URL |
allowed_ssh_cidrs |
CIDR blocks allowed for SSH | ["0.0.0.0/0"] |
volume_size |
Root EBS volume size (GB) | 50 |
environment |
Environment tag | production |
terraform apply \
-var="ssh_key_name=my-key" \
-var="aws_region=eu-west-1" \
-var="allowed_ssh_cidrs=[\"YOUR_IP/32\"]" \
-var="volume_size=100"Uncomment the instance_market_options block in terraform/ec2.tf for spot pricing.
| Resource | Description |
|---|---|
| VPC + Subnet | Isolated network with internet access |
| c5.metal Instance | Bare-metal EC2 with KVM support |
| Security Group | SSH (22) + HTTP (80) + HTTPS (443) |
| Elastic IP | Static public IP address |
| nginx | Reverse proxy on port 80 |
| Systemd Service | Auto-starts/restarts bouvet-mcp |
After deployment (~5 minutes for first boot):
# Get the public IP
PUBLIC_IP=$(terraform output -raw public_ip)
# Check health endpoint
curl -f http://$PUBLIC_IP/health
# Test MCP endpoint
curl -X POST http://$PUBLIC_IP/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# SSH for debugging
ssh -i ~/.ssh/my-key.pem admin@$PUBLIC_IP
sudo journalctl -u bouvet-mcp -fAfter terraform apply:
| Output | Description |
|---|---|
public_ip |
Elastic IP address |
mcp_endpoint |
Full MCP endpoint URL |
health_url |
Health check URL |
ssh_command |
Ready-to-use SSH command |
terraform destroy -var="ssh_key_name=my-key"For custom infrastructure or local bare-metal servers.
# Check KVM is available
ls -la /dev/kvm
# If not present, load the module
sudo modprobe kvm
sudo modprobe kvm_intel # or kvm_amd
# Make KVM accessible
sudo chmod 666 /dev/kvm
# Persistent permission (create udev rule)
echo 'KERNEL=="kvm", MODE="0666"' | sudo tee /etc/udev/rules.d/99-kvm.rules# Debian/Ubuntu - Install Docker CE from official repository
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
# Add Docker's official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add Docker repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker CE
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable docker
sudo systemctl start dockerdocker run -d \
--name bouvet-mcp \
--restart=unless-stopped \
--privileged \
--device=/dev/kvm \
-p 8080:8080 \
-e BOUVET_TRANSPORT=http \
-e BOUVET_HTTP_HOST=0.0.0.0 \
-e BOUVET_HTTP_PORT=8080 \
-e RUST_LOG=info \
ghcr.io/vrn21/bouvet-mcp:latestThe container will automatically download the rootfs image on first startup (~2GB).
# Check container is running
docker logs -f bouvet-mcp
# Test health endpoint
curl http://localhost:8080/health
# Test MCP
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Build your own rootfs image with custom tools:
# Clone the repository
git clone https://github.com/vrn21/bouvet.git
cd bouvet
# Build rootfs (runs in Docker, works on macOS)
make rootfs
# The image is created at:
# images/output/debian-devbox.ext4To use your custom rootfs:
Option 1: Mount as volume
docker run -d \
--privileged \
--device=/dev/kvm \
-v /path/to/your/rootfs.ext4:/var/lib/bouvet/debian-devbox.ext4 \
-p 8080:8080 \
ghcr.io/vrn21/bouvet-mcp:latestOption 2: Host on S3 or HTTP
docker run -d \
--privileged \
--device=/dev/kvm \
-e BOUVET_ROOTFS_URL=https://your-bucket.s3.amazonaws.com/rootfs.ext4 \
-p 8080:8080 \
ghcr.io/vrn21/bouvet-mcp:latest# ARM64 (Apple Silicon, Graviton)
make rootfs ARCH=aarch64
# x86_64 (Intel/AMD)
make rootfs ARCH=x86_64For production deployments without Docker:
# Create directories
sudo mkdir -p /var/lib/bouvet /usr/local/bin /tmp/bouvet
# Download Firecracker (adjust version/arch as needed)
FC_VERSION=1.5.1
ARCH=x86_64
curl -sSL https://github.com/firecracker-microvm/firecracker/releases/download/v${FC_VERSION}/firecracker-v${FC_VERSION}-${ARCH}.tgz | \
sudo tar -xz -C /tmp
sudo mv /tmp/release-v${FC_VERSION}-${ARCH}/firecracker-* /usr/local/bin/firecracker
sudo mv /tmp/release-v${FC_VERSION}-${ARCH}/jailer-* /usr/local/bin/jailer
# Download kernel (use amd64 or arm64 based on architecture)
KERNEL_ARCH=$([ "$ARCH" = "aarch64" ] && echo "arm64" || echo "amd64")
curl -sSL https://storage.googleapis.com/fireactions/kernels/${KERNEL_ARCH}/5.10/vmlinux | \
sudo tee /var/lib/bouvet/vmlinux > /dev/null
# Download rootfs
curl -sSL https://bouvet-artifacts.s3.us-east-1.amazonaws.com/debian-devbox.ext4 | \
sudo tee /var/lib/bouvet/debian-devbox.ext4 > /dev/null
# Build bouvet-mcp from source
cargo build --release -p bouvet-mcp
sudo cp target/release/bouvet-mcp /usr/local/bin/sudo tee /etc/systemd/system/bouvet-mcp.service << 'EOF'
[Unit]
Description=Bouvet MCP Server
After=network.target
[Service]
Type=simple
Restart=on-failure
RestartSec=10
Environment=BOUVET_KERNEL=/var/lib/bouvet/vmlinux
Environment=BOUVET_ROOTFS=/var/lib/bouvet/debian-devbox.ext4
Environment=BOUVET_FIRECRACKER=/usr/local/bin/firecracker
Environment=BOUVET_CHROOT=/tmp/bouvet
Environment=BOUVET_TRANSPORT=http
Environment=BOUVET_HTTP_HOST=0.0.0.0
Environment=BOUVET_HTTP_PORT=8080
Environment=RUST_LOG=info
ExecStart=/usr/local/bin/bouvet-mcp
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable bouvet-mcp
sudo systemctl start bouvet-mcpThe terraform/s3/ module creates a public S3 bucket for hosting rootfs images.
cd terraform/s3
terraform init
terraform apply -var="bucket_name=my-bouvet-artifacts"
# Upload your rootfs
aws s3 cp images/output/debian-devbox.ext4 s3://my-bouvet-artifacts/
# Your rootfs URL will be:
# https://my-bouvet-artifacts.s3.us-east-1.amazonaws.com/debian-devbox.ext4- Restrict SSH Access: Set
allowed_ssh_cidrsto your IP range - HTTPS: nginx is included; add SSL with
certbot --nginx -d yourdomain.com - VPC Peering: For internal-only access, keep the MCP endpoint private
The container runs as root to access /dev/kvm, but:
- Sandboxes are fully isolated microVMs (separate kernel, filesystem)
- No container escape possible through sandboxed code
- Each sandbox is destroyed after use
# Allow HTTP, HTTPS, and SSH
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enableERROR: /dev/kvm not found
Solution: Use a bare-metal instance or enable nested virtualization:
- AWS: Use
.metalinstance types (c5.metal,m5.metal, etc.) - GCP: Enable nested virtualization on the VM
- Local: Load KVM modules (
modprobe kvm kvm_intel)
Check logs:
docker logs bouvet-mcpCommon causes:
- Missing
/dev/kvm— add--device=/dev/kvmor--privileged - Insufficient disk space — ensure 3GB+ free for rootfs download
- Port already in use — change
BOUVET_HTTP_PORT
# Check Firecracker can start
sudo /usr/local/bin/firecracker --version
# Check KVM permissions
ls -la /dev/kvm
# Should show: crw-rw-rw- ... /dev/kvm# Verify the service is running
curl -v http://localhost:8080/health
# Check container/service logs
docker logs bouvet-mcp
# or
sudo journalctl -u bouvet-mcp -f| Instance | On-Demand | Spot (typical) | Notes |
|---|---|---|---|
| c5.metal | ~$4.08/hr | ~$0.80/hr | 96 vCPUs, 192GB RAM |
| m5.metal | ~$4.61/hr | ~$0.92/hr | 96 vCPUs, 384GB RAM |
| c6i.metal | ~$4.18/hr | ~$0.84/hr | Latest gen, 128 vCPUs |
Tip
Use Spot Instances for development and testing — up to 80% savings.
- Configuration Reference — All environment variables and options
- Architecture — Technical deep dive into Bouvet internals