This guide explains how to run Wassette in a Docker container for enhanced security isolation. Containerizing Wassette provides an additional layer of defense by isolating the runtime environment from your host system.
Running Wassette in Docker provides several benefits:
- Enhanced Security: Docker containers provide an additional isolation layer on top of Wassette's WebAssembly sandbox
- Reproducible Environment: Ensures consistent runtime behavior across different systems
- Easy Deployment: Simplifies deployment to production environments
- Resource Control: Allows fine-grained control over CPU, memory, and network resources
- Docker installed on your system (Install Docker)
- Basic familiarity with Docker commands
From the Wassette repository root:
docker build -t wassette:latest .This builds a multi-stage Docker image that:
- Compiles Wassette from source in a Rust build environment
- Creates a minimal runtime image with only necessary dependencies
- Runs as a non-root user for enhanced security
The Docker image defaults to streamable-http transport:
docker run --rm -p 9001:9001 wassette:latestThen connect to http://localhost:9001 from your MCP client.
For use with MCP clients that expect stdio, override the default command:
docker run -i --rm wassette:latest wassette serve --stdioFor SSE transport, override the default command:
docker run --rm -p 9001:9001 wassette:latest wassette serve --sseThen connect to http://localhost:9001/sse from your MCP client.
To use custom WebAssembly components with Wassette in Docker, you need to mount the component directory:
# Mount your local components directory
docker run -i --rm \
-v /path/to/your/components:/home/wassette/.local/share/wassette/components:ro \
wassette:latestImportant: Use :ro (read-only) for the component directory when possible to prevent accidental modifications.
# Build example components first (on host)
cd examples/filesystem-rs
cargo build --release --target wasm32-wasip2
# Run Wassette with the example component mounted (streamable-http transport)
docker run --rm -p 9001:9001 \
-v $(pwd)/examples/filesystem-rs/target/wasm32-wasip2/release:/home/wassette/.local/share/wassette/components:ro \
wassette:latest
# For stdio transport, override the default:
# docker run -i --rm \
# -v $(pwd)/examples/filesystem-rs/target/wasm32-wasip2/release:/home/wassette/.local/share/wassette/components:ro \
# wassette:latest wassette serve --stdioYou can mount multiple component directories using multiple -v flags:
docker run --rm -p 9001:9001 \
-v /path/to/components1:/home/wassette/.local/share/wassette/components:ro \
-v /path/to/data:/data:rw \
wassette:latestIf your components require secrets (API keys, credentials, etc.), mount the secrets directory:
docker run --rm -p 9001:9001 \
-v /path/to/secrets:/home/wassette/.config/wassette/secrets:ro \
-v /path/to/components:/home/wassette/.local/share/wassette/components:ro \
wassette:latestSecurity Note: Always mount secrets as read-only (:ro) and ensure proper file permissions.
Pass environment variables to the container:
docker run --rm -p 8080:8080 \
-e PORT=8080 \
-e BIND_HOST=0.0.0.0 \
-e RUST_LOG=debug \
-e OPENWEATHER_API_KEY=your_api_key \
wassette:latestTwelve-Factor App Compliance: Wassette supports PORT and BIND_HOST environment variables for flexible port binding. The Docker image defaults to BIND_HOST=0.0.0.0 to allow external connections.
These environment variables set the default bind address when not specified via CLI (--bind-address) or config file:
- PORT: Port number to listen on (default: 9001)
- BIND_HOST: Host address to bind to (default: 127.0.0.1; Docker image overrides to 0.0.0.0)
Precedence: CLI > Config file > PORT/BIND_HOST > Defaults (127.0.0.1:9001)
Example with custom port:
docker run --rm -p 3000:3000 \
-e PORT=3000 \
wassette:latestSee the Environment Variables reference for comprehensive examples and best practices.
Mount a custom configuration file:
docker run --rm -p 9001:9001 \
-v /path/to/config.toml:/home/wassette/.config/wassette/config.toml:ro \
wassette:latestExample config.toml:
# Directory where components are stored
component_dir = "/home/wassette/.local/share/wassette/components"
# Environment variables to be made available to components
[environment_vars]
API_KEY = "your_api_key"
LOG_LEVEL = "info"For more complex setups, use Docker Compose:
# docker-compose.yml
version: '3.8'
services:
wassette:
build: .
image: wassette:latest
ports:
- "9001:9001"
volumes:
# Mount component directory (read-only)
- ./components:/home/wassette/.local/share/wassette/components:ro
# Mount secrets directory (read-only)
- ./secrets:/home/wassette/.config/wassette/secrets:ro
# Mount config file (optional)
- ./config.toml:/home/wassette/.config/wassette/config.toml:ro
environment:
- RUST_LOG=info
# Default is streamable-http, but you can override:
# command: ["wassette", "serve", "--sse"]
# command: ["wassette", "serve", "--stdio"]
# Security: Run with limited resources
deploy:
resources:
limits:
cpus: '1.0'
memory: 512MRun with:
docker-compose upThe Dockerfile already configures Wassette to run as a non-root user (wassette:1000). Never run as root:
# Good: Uses default non-root user
docker run --rm -p 9001:9001 wassette:latest
# Bad: Don't do this!
# docker run -i --rm --user root wassette:latestMount component and secret directories as read-only when possible:
docker run --rm -p 9001:9001 \
-v /path/to/components:/home/wassette/.local/share/wassette/components:ro \
wassette:latestPrevent resource exhaustion by setting limits:
docker run --rm -p 9001:9001 \
--memory="512m" \
--cpus="1.0" \
--pids-limit=100 \
wassette:latestFor maximum security, run with a read-only root filesystem:
docker run --rm -p 9001:9001 \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=50m \
-v /path/to/components:/home/wassette/.local/share/wassette/components:ro \
wassette:latestDrop Linux capabilities that Wassette doesn't need:
docker run --rm -p 9001:9001 \
--cap-drop=ALL \
--security-opt=no-new-privileges:true \
wassette:latestUse AppArmor or SELinux for additional security:
# With AppArmor
docker run --rm -p 9001:9001 \
--security-opt apparmor=docker-default \
wassette:latest
# With SELinux
docker run --rm -p 9001:9001 \
--security-opt label=type:container_runtime_t \
wassette:latestIf you need a custom base image:
FROM rust:1.90-bookworm AS builder
# ... build stage ...
FROM your-custom-base:latest
# ... runtime stage ...Wassette provides health and readiness endpoints when running with StreamableHttp transport:
/health: Returns 200 OK if server is running/ready: Returns JSON with readiness status/info: Returns version and build information
Add health checks in Docker Compose:
# docker-compose.yml
services:
wassette:
# ... other config ...
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9001/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40sOr with Docker CLI:
docker run --rm -p 9001:9001 \
--health-cmd="curl -f http://localhost:9001/health || exit 1" \
--health-interval=30s \
--health-timeout=10s \
--health-retries=3 \
wassette:latestNote: Health endpoints are only available with --streamable-http transport (the default for the Docker image). SSE transport (--sse) is designed solely for event streaming and does not expose standard HTTP endpoints like /health.
For persistent component storage across container restarts:
# Create a named volume
docker volume create wassette-components
# Use the volume
docker run --rm -p 9001:9001 \
-v wassette-components:/home/wassette/.local/share/wassette/components \
wassette:latestIf you encounter permission errors when mounting volumes:
# Check the ownership of your mounted directories
ls -la /path/to/components
# Ensure the wassette user (UID 1000) can read the files
sudo chown -R 1000:1000 /path/to/componentsVerify the mount path matches Wassette's expected directory:
# Check inside the container
docker run -i --rm \
-v /path/to/components:/home/wassette/.local/share/wassette/components:ro \
wassette:latest sh -c "ls -la /home/wassette/.local/share/wassette/components"When using HTTP/SSE transport, ensure the port is properly exposed:
# Check if the port is listening
docker run -d --name wassette-test -p 9001:9001 wassette:latest wassette serve --sse
docker logs wassette-test
curl http://localhost:9001/sse
docker rm -f wassette-testFor faster builds, you can create a Dockerfile that uses pre-built Wassette binaries:
FROM debian:bookworm-slim
# Install runtime dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates \
libssl3 \
curl && \
rm -rf /var/lib/apt/lists/*
# Download and install Wassette binary
ARG WASSETTE_VERSION=latest
RUN curl -fsSL https://github.com/microsoft/wassette/releases/download/${WASSETTE_VERSION}/wassette-linux-x86_64 -o /usr/local/bin/wassette && \
chmod +x /usr/local/bin/wassette
# Create non-root user and directories
RUN useradd -m -u 1000 -s /bin/bash wassette && \
mkdir -p /home/wassette/.local/share/wassette/components && \
mkdir -p /home/wassette/.config/wassette/secrets && \
chown -R wassette:wassette /home/wassette
ENV HOME=/home/wassette
ENV XDG_DATA_HOME=/home/wassette/.local/share
ENV XDG_CONFIG_HOME=/home/wassette/.config
USER wassette
WORKDIR /home/wassette
EXPOSE 9001
CMD ["wassette", "serve", "--stdio"]This approach is faster as it doesn't require compiling from source.
- Learn about Wassette's permission system
- Explore component examples
- Read the CLI reference for all available commands
- Check the FAQ for common questions