One-Command Deployment: Self-Host Your AI Wallet with Docker and GHCR
Would you trust a third party with your AI agent's private keys? If that question makes you uncomfortable, you're already thinking about self-hosting your wallet infrastructure — and WAIaaS makes it genuinely practical with a single Docker command. This post walks through how to get a fully self-hosted Wallet-as-a-Service running on your own server, with your own keys, under your own rules.
Why Self-Hosting Your AI Wallet Actually Matters
The rise of autonomous AI agents changes the stakes around custody. When a human manages a wallet, they can pause, verify, and think before signing. An AI agent operates continuously, potentially making hundreds of transactions — so the infrastructure holding those keys becomes critically important.
Hosted wallet services make a trade-off: you get convenience in exchange for trusting someone else's server, someone else's rate limits, and someone else's uptime SLA. For many teams building experimental agents, that's fine. But for anyone running production workloads, handling real funds, or operating in environments with strict data residency requirements, the calculus shifts. Self-hosting gives you:
- Full key custody — private keys never leave your infrastructure
- No rate limits imposed by a third party — your server, your throughput
- Auditability — WAIaaS is open-source, so you can read every line of code handling your keys
- Network control — bind to localhost, put it behind a VPN, restrict egress however you want
WAIaaS is built specifically for this use case: a self-hosted, open-source Wallet-as-a-Service designed for AI agents, deployable in one command.
The One-Command Start
The Docker image is published to GitHub Container Registry (GHCR) at ghcr.io/minhoyoo-iotrust/waiaas:latest. The fastest path to a running instance is:
git clone https://github.com/minhoyoo-iotrust/WAIaaS.git
cd WAIaaS
docker compose up -d
That's it. The daemon starts on port 3100, bound to 127.0.0.1 by default — so it's only accessible from localhost until you explicitly expose it. This is a deliberate choice: the default posture is private.
If you don't want to clone the repo and just want to run the image directly, the auto-provision flag handles first-run setup without any interactive prompts:
docker run -d \
--name waiaas \
-p 127.0.0.1:3100:3100 \
-v waiaas-data:/data \
-e WAIAAS_AUTO_PROVISION=true \
ghcr.io/minhoyoo-iotrust/waiaas:latest
# Retrieve auto-generated master password
docker exec waiaas cat /data/recovery.key
WAIAAS_AUTO_PROVISION=true tells the entrypoint to generate a random master password on first start and write it to /data/recovery.key. You retrieve it once, store it securely, and the daemon is ready. No wizard, no web UI required at setup time.
Understanding the Docker Compose Setup
For anything beyond a quick experiment, you'll want to use the Compose file. Here's what the default configuration looks like:
services:
daemon:
image: ghcr.io/minhoyoo-iotrust/waiaas:latest
container_name: waiaas-daemon
ports:
- "127.0.0.1:3100:3100"
volumes:
- waiaas-data:/data
environment:
- WAIAAS_DATA_DIR=/data
- WAIAAS_DAEMON_HOSTNAME=0.0.0.0
env_file:
- path: .env
required: false
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3100/health"]
interval: 30s
timeout: 5s
start_period: 10s
retries: 3
volumes:
waiaas-data:
driver: local
A few things worth noting for the self-hoster audience:
Healthcheck is built in. The compose file includes a curl-based healthcheck out of the box. Your orchestrator or monitoring stack can rely on this without any extra configuration.
Named volume for persistence. Wallet data, session tokens, and configuration live in the waiaas-data named volume. docker compose down preserves this volume. You need docker compose down -v to actually delete your data — which means accidental container restarts don't lose anything.
Non-root process. The daemon runs as UID 1001 inside the container, not as root. This matters for security-conscious deployments where host filesystem permissions are locked down.
restart: unless-stopped means the daemon comes back after a server reboot automatically, which is what you want for a persistent agent backend.
Production Secrets: Not in Environment Variables
Putting secrets in environment variables is convenient but leaves them visible in docker inspect output and process listings. For production deployments, WAIaaS ships with a Docker Secrets overlay:
# Create secret files
mkdir -p secrets
echo "your-secure-password" > secrets/master_password.txt
chmod 600 secrets/master_password.txt
# Deploy with secrets overlay
docker compose -f docker-compose.yml -f docker-compose.secrets.yml up -d
The docker-compose.secrets.yml overlay file maps the secret into the container via Docker's secrets mechanism rather than environment variables. The entrypoint script knows how to read from Docker Secrets automatically — this is one of the features the entrypoint explicitly supports alongside auto-provision.
Key Environment Variables for Your Setup
Self-hosters typically want to tune a few things out of the box:
WAIAAS_AUTO_PROVISION=true # Auto-generate master password on first start
WAIAAS_DAEMON_PORT=3100 # Listening port
WAIAAS_DAEMON_HOSTNAME=0.0.0.0 # Bind address (use 127.0.0.1 for localhost-only)
WAIAAS_DAEMON_LOG_LEVEL=info # Log level: trace/debug/info/warn/error
WAIAAS_DATA_DIR=/data # Data directory
WAIAAS_RPC_SOLANA_MAINNET=<url> # Solana mainnet RPC endpoint
WAIAAS_RPC_EVM_ETHEREUM_MAINNET=<url> # Ethereum mainnet RPC endpoint
The RPC endpoint variables are particularly important: you can point WAIaaS at your own RPC nodes, a private Alchemy/Infura endpoint, or any compatible provider. Your traffic doesn't have to go through any shared infrastructure.
Verifying Your Installation
Once the daemon is running, confirm everything is healthy:
# Check logs
docker compose logs -f
# Hit the health endpoint
curl http://127.0.0.1:3100/health
# View the auto-generated interactive API docs
open http://127.0.0.1:3100/reference
That /reference endpoint is genuinely useful — WAIaaS auto-generates an OpenAPI 3.0 spec and serves an interactive Scalar API reference UI. You can explore all 39 REST API route modules directly from your browser without reading a single line of documentation. The raw spec is also available:
curl http://127.0.0.1:3100/doc -o openapi.json
Creating Your First Wallet and Agent Session
With the daemon running, create a wallet and wire up an agent session. The REST API uses three auth methods depending on what you're doing — master password for administrative operations, session tokens for AI agents, and owner signatures for approvals.
# Create a wallet (requires master password)
curl -X POST http://127.0.0.1:3100/v1/wallets \
-H "Content-Type: application/json" \
-H "X-Master-Password: my-secret-password" \
-d '{"name": "trading-wallet", "chain": "solana", "environment": "mainnet"}'
# Create a session token for your AI agent
curl -X POST http://127.0.0.1:3100/v1/sessions \
-H "Content-Type: application/json" \
-H "X-Master-Password: my-secret-password" \
-d '{"walletId": "<wallet-uuid>"}'
The session token returned here is what your AI agent uses for all subsequent operations — checking balances, sending transactions, executing DeFi actions. The master password stays on the administrative side and never needs to be handed to agent code.
# Agent checks its balance using the session token
curl http://127.0.0.1:3100/v1/wallet/balance \
-H "Authorization: Bearer wai_sess_eyJhbGciOiJIUzI1NiJ9..."
Setting Spending Limits Before You Let Agents Loose
This is the step self-hosters often skip and later regret. Before your AI agent touches real funds, set a spending policy:
curl -X POST http://127.0.0.1:3100/v1/policies \
-H "Content-Type: application/json" \
-H "X-Master-Password: my-secret-password" \
-d '{
"walletId": "<wallet-uuid>",
"type": "SPENDING_LIMIT",
"rules": {
"instant_max_usd": 100,
"notify_max_usd": 500,
"delay_max_usd": 2000,
"delay_seconds": 900,
"daily_limit_usd": 5000
}
}'
WAIaaS has a policy engine with 21 policy types and 4 security tiers (INSTANT, NOTIFY, DELAY, APPROVAL). The SPENDING_LIMIT policy shown above creates a tiered response: small transactions go through immediately, medium ones send a notification, larger ones are queued with a 15-minute delay before execution (giving you time to cancel), and anything above the threshold requires explicit human approval. The policy engine is default-deny for token transfers — if you haven't configured ALLOWED_TOKENS, transfers are blocked.
Connecting Claude (or Any MCP-Compatible Agent)
WAIaaS ships with 45 MCP tools, and the CLI makes wiring them to Claude Desktop nearly automatic:
npm install -g @waiaas/cli
waiaas mcp setup --all # Auto-register all wallets with Claude Desktop
Or configure manually in claude_desktop_config.json:
{
"mcpServers": {
"waiaas": {
"command": "npx",
"args": ["-y", "@waiaas/mcp"],
"env": {
"WAIAAS_BASE_URL": "http://127.0.0.1:3100",
"WAIAAS_SESSION_TOKEN": "wai_sess_<your-token>",
"WAIAAS_DATA_DIR": "~/.waiaas"
}
}
}
}
The WAIAAS_BASE_URL points directly at your local daemon — no traffic leaves your machine unless the agent itself initiates a blockchain transaction.
The Five-Step Quick Start
For anyone who skimmed here: the minimum path to a running self-hosted AI wallet is:
1. Pull and start the container:
docker run -d \
--name waiaas \
-p 127.0.0.1:3100:3100 \
-v waiaas-data:/data \
-e WAIAAS_AUTO_PROVISION=true \
ghcr.io/minhoyoo-iotrust/waiaas:latest
2. Get your master password:
docker exec waiaas cat /data/recovery.key
3. Create a wallet and session (using the curl commands above)
4. Set a spending policy before funding the wallet
5. Connect your agent via MCP or the TypeScript/Python SDK
The entire flow from zero to a connected AI agent with policy-governed spending takes under 10 minutes on a machine that already has Docker.
What's Running Under the Hood
WAIaaS is a 15-package monorepo. The Docker image you're pulling contains the daemon package — a REST API server with 39 route modules, a 7-stage transaction pipeline (validate → auth → policy → wait → execute → confirm), and support for 2 chain types across 18 networks. It also bundles 15 DeFi protocol integrations including Aave v3, Jupiter, Hyperliquid, Lido, and Jito, among others.
The second Docker image in the project — push-relay — handles push notification delivery for transaction approval workflows. You don't need it to get started, but it's what enables the APPROVAL security tier to route notifications to your phone when an agent tries to execute a large transaction.
The Philosophy of Running Your Own
There's a reason the self-hosting community reaches for phrases like "your keys, your coins." It's not just about distrust of any particular service — it's about the principle that infrastructure handling private keys should be auditable, controllable, and replaceable. WAIaaS is open-source under a license you can read, deployed from a GHCR image whose Dockerfile is in the repository, running on hardware you control.
The crypto equivalent of running your own email server used to be genuinely painful. Modern tooling has changed that calculus for email, and WAIaaS is trying to do the same for AI agent wallet infrastructure. One Docker command, a named volume for persistence, built-in healthchecks, Docker Secrets for production — the operational burden is low enough that "self-hosted" is no longer synonymous with "someone's full-time job."
What's Next
Explore the full source and contribute at GitHub, and check the project site at waiaas.ai for documentation and announcements. Once your daemon is running, the natural next step is exploring the policy engine in depth — 21 policy types give you granular control over exactly what your agents can and cannot do with your funds.
Top comments (0)