Running Wassette in Docker

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.

Why Use Docker with Wassette?

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

Prerequisites

  • Docker installed on your system (Install Docker)
  • Basic familiarity with Docker commands

Quick Start

Build the Docker Image

From the Wassette repository root:

docker build -t wassette:latest .

This builds a multi-stage Docker image that:

  1. Compiles Wassette from source in a Rust build environment
  2. Creates a minimal runtime image with only necessary dependencies
  3. Runs as a non-root user for enhanced security

Run with Streamable HTTP Transport (Default)

The Docker image defaults to streamable-http transport:

docker run --rm -p 9001:9001 wassette:latest

Then connect to http://localhost:9001 from your MCP client.

Run with Stdio Transport

For use with MCP clients that expect stdio, override the default command:

docker run -i --rm wassette:latest wassette serve --stdio

Run with SSE Transport

For SSE transport, override the default command:

docker run --rm -p 9001:9001 wassette:latest wassette serve --sse

Then connect to http://localhost:9001/sse from your MCP client.

Mounting Components

To use custom WebAssembly components with Wassette in Docker, you need to mount the component directory:

Mount a Local Component Directory

# Mount your local components directory
docker run -i --rm \
  -v /path/to/your/components:/home/wassette/.local/share/wassette/components:ro \
  wassette:latest

Important: Use :ro (read-only) for the component directory when possible to prevent accidental modifications.

Example: Running with Filesystem Component

# 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 --stdio

Example: Running with Multiple Component Directories

You 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:latest

Mounting Secrets

If 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:latest

Security Note: Always mount secrets as read-only (:ro) and ensure proper file permissions.

Configuration

Using Environment Variables

Pass environment variables to the container:

docker run --rm -p 9001:9001 \
  -e RUST_LOG=debug \
  -e OPENWEATHER_API_KEY=your_api_key \
  wassette:latest

See the Environment Variables reference for comprehensive examples and best practices.

Using a Configuration File

Mount a custom configuration file:

docker run --rm -p 9001:9001 \
  -v /path/to/config.toml:/home/wassette/.config/wassette/config.toml:ro \
  wassette:latest

Example config.toml:

# Directory where components are stored
plugin_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"

Docker Compose

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: 512M

Run with:

docker-compose up

Security Best Practices

1. Run as Non-Root User

The 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:latest

2. Use Read-Only Mounts

Mount 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:latest

3. Limit Container Resources

Prevent resource exhaustion by setting limits:

docker run --rm -p 9001:9001 \
  --memory="512m" \
  --cpus="1.0" \
  --pids-limit=100 \
  wassette:latest

4. Use Read-Only Root Filesystem

For 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:latest

5. Drop Unnecessary Capabilities

Drop Linux capabilities that Wassette doesn’t need:

docker run --rm -p 9001:9001 \
  --cap-drop=ALL \
  --security-opt=no-new-privileges:true \
  wassette:latest

6. Enable Security Profiles

Use 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:latest

Advanced Usage

Multi-Stage Build with Custom Base

If you need a custom base image:

FROM rust:1.90-bookworm AS builder
# ... build stage ...

FROM your-custom-base:latest
# ... runtime stage ...

Health Checks

Add health checks when running with HTTP/SSE transport:

# docker-compose.yml
services:
  wassette:
    # ... other config ...
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9001/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Persistent Component Storage

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:latest

Troubleshooting

Permission Denied Errors

If 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/components

Container Cannot Access Components

Verify 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"

Network Connectivity Issues

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-test

Building from Pre-Built Binaries

For 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.

Next Steps

Resources