Docker Deployment Guide
GAIA uses Docker Compose to orchestrate all the required services. This makes deployment simple and ensures consistency across different environments.Looking for an easier setup? The GAIA CLI provides a guided wizard that handles Docker setup automatically, including prerequisite checks, environment variable discovery, and service management. This guide covers manual Docker setup for advanced users who prefer full control.
Prerequisites
Docker Installation
Docker Installation
Install Docker Desktop (recommended for beginners):Or install Docker Engine (for servers):
System Requirements
System Requirements
Minimum Requirements:
- 2 CPU cores
- 4GB RAM
- 10GB free disk space
- Docker Engine 20.10+
- Docker Compose v2.0+
- 4+ CPU cores
- 8+ GB RAM
- 50+ GB SSD storage
- Regular backups configured
Project Structure
When using the CLI quick start, running
gaia init with Self-Host (Docker)
scaffolds this structure automatically.Quick Start
Verify and monitor
- GAIA Web: http://localhost:3000
- Backend API: http://localhost:8000
- API Docs: http://localhost:8000/docs
- ChromaDB: http://localhost:8080
- RabbitMQ Management: http://localhost:15672 (guest/guest)
- Mongo Express: http://localhost:8081 (admin/password)
Service Overview
The Docker Compose setup includes the following services:Web (gaia-web)
Web (gaia-web)
Next.js React Application
- Port: 3000
- Hot reload enabled in development
- Serves the web interface
Backend (gaia-backend)
Backend (gaia-backend)
FastAPI Python Application
- Port: 8000
- Auto-reload enabled in development
- RESTful API and WebSocket support
Databases
Databases
PostgreSQL (Port: 5432)
- Stores user data and application state
- User: postgres, Password: postgres, DB: langgraph
- Document storage and metadata
- No authentication in development
- Caching and session storage
- No authentication in development
- Vector database for embeddings
- Persistent storage in Docker volume
Background Workers
Background Workers
Worker - Handles scheduled tasks and background processing
ARQ Worker - Async task queue processing
RabbitMQ - Message broker (Port: 5672, Management: 15672)
Persistent Workspace Storage (JuiceFS and FUSE)
GAIA’s per-user workspace (file uploads, agent artifacts, installed skills) is backed by JuiceFS, a filesystem that stores file data in object storage (Cloudflare R2, S3, or any S3-compatible store) and file metadata in a database. The backend mounts it at/mnt/jfs and reads and writes files there like a local disk.
JuiceFS mounts through FUSE (Filesystem in Userspace), a Linux kernel feature. This has direct consequences for where and how you can run the backend.
Run the backend in a container
Because the mount needs Linux FUSE, run the backend in a container on every platform:| Host OS | Native (uvicorn on the host) | Containerized (Docker) |
|---|---|---|
| Linux | Works if juicefs is installed and /dev/fuse is accessible | Works |
| macOS | Not supported (no Linux FUSE) | Works, Docker Desktop’s Linux VM provides /dev/fuse |
| Windows | Not supported (no Linux FUSE) | Works, Docker Desktop / WSL2 provides /dev/fuse |
Run the backend in a container even on macOS and Windows. Docker Desktop runs a Linux VM that exposes
/dev/fuse, so the FUSE mount works inside the container even though the host OS cannot mount it directly.How the mount works
FUSE lets a userspace program act as a filesystem instead of the kernel:- The kernel exposes a device at
/dev/fuse. - The
juicefs mountprocess opens that device and attaches itself as the driver for/mnt/jfs. - When the backend reads or writes a path under
/mnt/jfs, the kernel routes the call to the JuiceFS process. - JuiceFS looks up metadata in its database, fetches or stores the file’s chunks in object storage, and serves the result back through
/dev/fuse.
/mnt/jfs.
Container settings that make it work
A Docker container is unprivileged by default: it cannot mount filesystems, cannot see host devices, and runs under an AppArmor profile that blocksmount. The Compose file grants exactly what FUSE needs and nothing more:
cap_add: SYS_ADMIN
cap_add: SYS_ADMIN
The
mount(2) syscall requires the CAP_SYS_ADMIN capability, which Docker drops by default. Without it, juicefs cannot perform the mount and fails before it starts. This grants only that one capability, not full privileged access.devices: /dev/fuse
devices: /dev/fuse
The JuiceFS process opens
/dev/fuse to talk to the kernel’s FUSE module. Containers see no host devices by default, so this passes the device through. Without it you get fusermount3: cannot open /dev/fuse: No such file or directory.security_opt: apparmor:unconfined
security_opt: apparmor:unconfined
On Ubuntu and Debian hosts, Docker applies a default AppArmor profile that denies the
mount operation even when the container holds CAP_SYS_ADMIN. This lifts that profile. On hosts without AppArmor it has no effect.volumes: juicefs_cache
volumes: juicefs_cache
JuiceFS keeps a local disk cache of file chunks so repeat reads don’t go back to object storage. Persisting it in a named volume keeps the cache warm across restarts. This affects speed only, not correctness.
Required configuration
The container settings make a mount possible. JuiceFS still needs an object store and a metadata database before it will mount. Set these inapps/api/.env:
| Variable | Purpose |
|---|---|
R2_ACCOUNT_ID, R2_BUCKET, R2_ACCESS_KEY, R2_SECRET_KEY | Cloudflare R2 (or S3-compatible) object store for file data |
JUICEFS_META_URL_TEMPLATE | Connection URL for the metadata database (a PostgreSQL DB works well) |
JuiceFSUnavailable. See Environment Variables for the full list.
Docker Commands
Starting and Stopping
Viewing Logs
Service Management
Development Mode
For development, use the defaultdocker-compose.yml:
- Hot reload for both frontend and backend
- Source code mounted as volumes
- Debug logging enabled
- Development databases with default credentials
Health Checks
All services include health checks. Monitor service health:Data Persistence
Data is persisted using Docker volumes:pgdata- PostgreSQL datamongo_data- MongoDB dataredis_data- Redis datachroma_data- ChromaDB vectorsrabbitmq_data- RabbitMQ messages
Networking
Services communicate through thegaia_network bridge network:
Troubleshooting
Logs show JuiceFSUnavailable
Logs show JuiceFSUnavailable
fusermount3: cannot open /dev/fuse
fusermount3: cannot open /dev/fuse
The FUSE device isn’t reaching the container. Confirm the
devices: /dev/fuse:/dev/fuse mapping is present. On a Linux host, verify the device exists with ls -l /dev/fuse and load the module if missing: sudo modprobe fuse. On macOS and Windows, make sure Docker Desktop is running (it provides the device from its Linux VM).Mount fails with permission denied
Mount fails with permission denied
The container holds the capability but the host’s security profile is blocking the mount. Confirm
security_opt: apparmor:unconfined is set. On SELinux hosts, you may also need to allow the mount in your local policy.Alternative: CLI Setup
For a simplified setup experience with automatic configuration, see the CLI Setup Guide. The CLI handles all the Docker commands, environment setup, and health checks automatically.Next Steps
Environment Variables
Configure your API keys and settings

