Run GRAIN relay with Docker using either the latest stable release or pre-release binaries. Starting with v0.5.0, GRAIN is zero-dependency and does not require an external database like MongoDB.
- Quick Start
- Docker Compose Configuration
- Data Persistence
- Manual Docker Build Commands
- Configuration
- Viewing Logs
- Management Commands
- Security Considerations
- Troubleshooting
mkdir grain-docker
cd grain-docker# Download Dockerfile and docker-compose.yml
curl -O https://raw.githubusercontent.com/0ceanslim/grain/main/docs/docker/Dockerfile
curl -O https://raw.githubusercontent.com/0ceanslim/grain/main/docs/docker/docker-compose.ymldocker compose up -dYour relay is now running at:
- WebSocket:
ws://localhost:8181 - Web:
http://localhost:8181
Starting with v0.5.0, the docker-compose.yml is significantly simplified as it no longer requires a MongoDB service.
version: "3.8"
services:
grain:
build:
context: .
dockerfile: Dockerfile
ports:
- "8181:8181"
environment:
- GRAIN_ENV=production
- LOG_LEVEL=info
volumes:
- grain_data:/home/grain/.grain
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8181/"]
interval: 30s
timeout: 10s
retries: 3
volumes:
grain_data:GRAIN v0.5.0 uses an embedded nostrdb engine. It is critical to use a Docker volume to persist your data, otherwise your database and configurations will be lost when the container is removed.
The default docker-compose.yml mounts a named volume on /home/grain/.grain, which is GRAIN's data directory inside the container — it holds config.yml, blacklist.yml, whitelist.yml, relay_metadata.json, the LMDB store under data/, and the runtime log file debug.log. Mounting the entire directory persists the relay's full operational state across container recreation.
GRAIN automatically creates default configuration files on first startup.
You can set basic options directly in docker-compose.yml:
GRAIN_ENV:productionordevelopmentLOG_LEVEL:debug,info,warn,errorSERVER_PORT: Internal port (default 8181)
Bind a local directory to the container's data directory to manage config files from the host:
services:
grain:
volumes:
- ./my-grain-config:/home/grain/.grainYou can then edit config.yml, whitelist.yml, etc., directly on your host machine. GRAIN supports hot-reloading, so changes are applied instantly without restarting the container.
The host directory should be writable by uid/gid 1001:1001 (the non-root grain user inside the container). On first run GRAIN will populate the directory with default config files if it is empty.
GRAIN logs to both stdout (minimal) and a structured log file.
docker compose logs -f grainBy default the relay only writes to its log file inside the container, so docker logs shows just the startup line. To mirror the live log to stdout (so docker compose logs -f grain shows everything in real time), set stdout: true under logging: in config.yml:
logging:
stdout: trueThe file sink is unaffected — the canonical debug.log continues to be written; stdout just gets a copy.
# View real-time application activity from the file
docker exec grain-relay tail -f /home/grain/.grain/debug.logTo update to the latest version:
docker compose pull
docker compose up -dSince storage is embedded, you can perform maintenance via the GRAIN CLI:
# Check database stats
docker exec grain-relay ./grain --stats
# Physically delete an event
docker exec grain-relay ./grain --delete <event_id>The Dockerfile automatically detects amd64 or arm64. If the build fails, ensure your Docker version supports multi-platform builds or check your internet connectivity to GitHub Releases.
If port 8181 is already in use, change the host mapping in docker-compose.yml:
ports:
- "9090:8181" # Map host 9090 to container 8181If the container crashes and won't restart, ensure no other process is accessing the data/ volume. LMDB (used by nostrdb) requires exclusive access to its lock files.
GRAIN Docker Setup Complete! 🌾