10 KiB
RustBox Docker
Run all commands from the docker/ directory.
Most Compose files use the project name rustbox; the HA/Garage example uses rustbox-ha. The examples pass -p explicitly so an exported COMPOSE_PROJECT_NAME cannot attach RustBox to an unrelated Compose project/network.
Quick start
Two RustBox nodes + Envoy + Valkey + PostgreSQL + OpenSearch + Garage S3:
PORT=3009 \
IMAP_HOST=mail.example.com \
SMTP_HOST=mail.example.com \
docker compose -p rustbox-ha -f compose-ha-garage.yml up --build
Verify load balancing and the selected backend header:
curl -sSI http://localhost:3009/ | grep -i '^x-rustbox-backend:'
curl -sSI http://localhost:3009/ | grep -i '^x-rustbox-backend:'
PostgreSQL + S3/SILO + OpenSearch:
PORT=3009 \
IMAP_HOST=mail.example.com \
SMTP_HOST=mail.example.com \
docker compose -p rustbox -f compose-postgres-s3-opensearch.yml up --build
SQLite, local blobs and local Tantivy:
IMAP_HOST=mail.example.com \
SMTP_HOST=mail.example.com \
docker compose -p rustbox -f compose-single-sqlite.yml up --build
MySQL + NFS + OpenSearch:
NFS_SERVER=10.0.0.10 \
NFS_EXPORT=/exports/rustbox \
IMAP_HOST=mail.example.com \
SMTP_HOST=mail.example.com \
docker compose -p rustbox -f compose-mysql-nfs-opensearch.yml up --build
Check only the RustBox project:
docker compose -p rustbox -f compose-postgres-s3-opensearch.yml ps
Stop it without deleting persistent volumes:
docker compose -p rustbox -f compose-postgres-s3-opensearch.yml down
To also delete the Compose volumes, use down -v only when you really want to remove local database, S3 and OpenSearch data.
Environment variables
Copy the supplied template if you prefer a persistent local configuration:
cp .env.example .env
| Variable | Default | Used by | Description |
|---|---|---|---|
HOST |
0.0.0.0 |
all | Address RustBox listens on inside the container. |
PORT |
3001 |
all | Published HTTP port. Single-node examples also use it as the RustBox listener; HA keeps RustBox on 3001 internally and maps ${PORT} to Envoy 8080. |
IMAP_HOST |
mail.example.com |
all | IMAP server hostname used for mailbox access/authentication. Set this for a real mail system. |
IMAP_PORT |
993 |
all | IMAP server port. |
SMTP_HOST |
mail.example.com |
all | SMTP server hostname used for sending mail. |
SMTP_PORT |
587 |
all | SMTP submission port. |
TLS_ENABLED |
true |
all | Enables TLS for mail connections according to RustBox configuration. |
RUST_LOG |
info |
all | Rust logging filter/level. |
SESSION_ENCRYPTION_KEY |
development key | PostgreSQL, MySQL | Shared key used to encrypt session data. All application nodes must use the same value. Replace it outside local testing. |
POSTGRES_PASSWORD |
rustbox-dev-password |
PostgreSQL | Password for PostgreSQL user rustbox; also used in DATABASE_URL. |
S3_ACCESS_KEY |
rustbox |
PostgreSQL/S3 | Access key for the bundled S3-compatible SILO service and RustBox S3 client. |
S3_SECRET_KEY |
rustbox-s3-dev-password |
PostgreSQL/S3 | Secret key for SILO and RustBox S3 client. Replace it outside local testing. |
MYSQL_PASSWORD |
rustbox-dev-password |
MySQL/NFS | Password for MySQL user rustbox; also used in DATABASE_URL. |
MYSQL_ROOT_PASSWORD |
rustbox-root-password |
MySQL/NFS | MySQL root password used by the container and health check. |
NFS_SERVER |
none | MySQL/NFS | NFS server address. Required for compose-mysql-nfs-opensearch.yml. |
NFS_EXPORT |
/rustbox |
MySQL/NFS | Export path on the NFS server. |
GARAGE_ACCESS_KEY_ID |
development key | HA/Garage | Garage S3 access key imported by the one-shot initializer. Replace outside local testing. |
GARAGE_SECRET_ACCESS_KEY |
development secret | HA/Garage | Garage S3 secret key. Replace outside local testing. |
GARAGE_CAPACITY |
10G |
HA/Garage | Capacity assigned to the single Garage node for layout balancing. |
SIEVE_ENABLED |
false |
HA/Garage | Enables ManageSieve routes/UI on both RustBox nodes. |
DIAGNOSTICS_ENABLED |
true |
HA/Garage | Enables diagnostics routes/UI. |
ABOUT_ENABLED |
true |
HA/Garage | Enables the About panel; keyboard shortcuts stay available independently. |
SECURITY_HEADERS_ENABLED |
false |
HA/Garage | Enables RustBox browser security headers. |
COMPOSE_PROJECT_NAME |
rustbox in .env.example |
Compose | Compose project name. A value exported in the shell overrides the file's name:. The documented commands use -p rustbox, which has the highest priority. |
RUSTBOX_CONFIG and DATABASE_URL are set by the Compose files themselves and normally should not be supplied from the shell for these bundled topologies.
Avoiding another Compose project
If docker compose ... ps shows containers from another application, inspect the effective project-name override:
printf 'COMPOSE_PROJECT_NAME=%s\n' "${COMPOSE_PROJECT_NAME:-<unset>}"
docker compose -p rustbox -f compose-postgres-s3-opensearch.yml ps
For RustBox use -p rustbox in operational commands. This isolates service discovery too: the RustBox container resolves PostgreSQL as postgres only on the RustBox Compose network.
Useful DNS/database checks:
docker compose -p rustbox -f compose-postgres-s3-opensearch.yml \
run --rm --no-deps --entrypoint getent rustbox hosts postgres
docker compose -p rustbox -f compose-postgres-s3-opensearch.yml \
exec postgres pg_isready -U rustbox -d rustbox
Topologies
compose-ha-garage.yml
Runs two RustBox application containers behind Envoy round-robin load balancing. PostgreSQL stores account/application state, Valkey stores shared sessions, OpenSearch provides the shared search index, and Garage provides S3-compatible blob storage. Only Envoy publishes a host port; the application nodes stay internal.
Envoy adds X-RustBox-Backend with the selected upstream address to every proxied response. WebSocket upgrades are enabled and Envoy health-checks /api/health on both nodes. The example uses a fixed Docker subnet because Garage advertises its RPC address; change 172.28.80.0/24 in both the Compose file and configs/ha-garage.toml if it conflicts with your network.
Garage is a single storage node in this local example. The application tier is redundant, but the backing services are not an HA production cluster by themselves.
compose-single-sqlite.yml
Single RustBox node with SQLite, filesystem blob storage and local Tantivy search. Persistent application data is stored in the rustbox-data Docker volume.
compose-postgres-s3-opensearch.yml
Cluster-ready backing topology with PostgreSQL, SILO providing an S3-compatible API, and OpenSearch. The RustBox application container itself has no persistent data_dir volume.
The bundled SILO topology is intended for local/integration validation of the S3 API. Production deployments should use a security-reviewed S3-compatible service with an appropriate lifecycle and HA policy.
compose-mysql-nfs-opensearch.yml
MySQL, shared POSIX/NFS blob storage and OpenSearch. The NFS volume driver is deliberate: a normal local Docker named volume is not shared between Docker hosts and therefore is not a valid blob backend for a multi-host RustBox deployment. These examples intentionally run one RustBox application container; the shared backing services demonstrate storage/database topology, not multi-app load balancing.
Production notes
Replace all example passwords, S3 credentials and SESSION_ENCRYPTION_KEY before production use.
The OpenSearch examples disable its security plugin to keep the local Compose topology self-contained. Production should use TLS/authentication and HTTPS URLs in search.urls.
The PostgreSQL/SILO and MySQL/NFS examples intentionally start one RustBox application container. compose-ha-garage.yml demonstrates two application nodes sharing SQL, sessions, blob storage and search behind Envoy. For production, make the backing PostgreSQL/Valkey/OpenSearch/Garage services appropriately redundant and terminate TLS before or at the load balancer.
Sessions and HTTP/HTTPS
Docker Compose defaults to ENVIRONMENT=development. In this mode session cookies do not carry the Secure attribute, so login works when RustBox is opened directly over plain HTTP, for example http://localhost:3009.
For a production deployment behind HTTPS set:
ENVIRONMENT=production docker compose -p rustbox -f compose-postgres-s3-opensearch.yml up -d --build
For the HA example, expose Envoy only behind HTTPS/TLS termination and use:
ENVIRONMENT=production docker compose -p rustbox-ha -f compose-ha-garage.yml up -d --build
In production, RustBox intentionally marks authentication cookies as Secure. Opening the application directly over plain HTTP in that mode causes the browser not to send the session cookie and protected requests return Invalid or expired session.
After changing from production/HTTP to development/HTTP, log in again. If the browser still has stale RustBox state, clear cookies/site data for the RustBox origin once and reload.
OpenSearch disk watermark / 429 errors
The opensearch-data named volume still consumes space on the filesystem used by Docker. Running OpenSearch in a container does not give it an independent disk. If the Docker host filesystem reaches OpenSearch's flood-stage threshold, OpenSearch protects indices with read_only_allow_delete and RustBox indexing receives HTTP 429 responses.
Check the actual filesystem seen by the container:
docker compose -p rustbox -f compose-postgres-s3-opensearch.yml exec opensearch \
df -h /usr/share/opensearch/data
Also inspect Docker usage on the host:
df -h
docker system df
docker volume inspect rustbox_opensearch-data
Free disk space first. Do not permanently lower the OpenSearch flood-stage safety threshold. If the index remains read-only after disk usage is back below the high watermark, clear the block explicitly:
docker compose -p rustbox -f compose-postgres-s3-opensearch.yml exec opensearch \
curl -fsS -X PUT http://localhost:9200/rustbox-messages-v1/_settings \
-H 'Content-Type: application/json' \
-d '{"index.blocks.read_only_allow_delete":null}'
RustBox 0.2.6 retains the OpenSearch outbox backoff introduced in 0.2.2, so these 429/flood-stage responses no longer produce one warning per queued event on a full disk.