Skip to main content

Deploy with Docker

Build the Docker Image

The project includes a multi-stage Dockerfile that builds both the CLI binary and the Python wheel:

# ---------- Stage 1: Builder ----------
FROM rust:slim-bookworm AS builder

RUN apt-get update && apt-get install -y \
build-essential libssl-dev pkg-config \
python3 python3-dev python3-pip python3-venv \
&& rm -rf /var/lib/apt/lists/*

RUN pip3 install --no-cache-dir --break-system-packages maturin

WORKDIR /build
COPY . .

RUN cargo build --release -p apiary-cli
RUN maturin build --release

# ---------- Stage 2: Runtime ----------
FROM python:3.11-slim-bookworm

RUN apt-get update && apt-get install -y \
libssl3 ca-certificates \
&& rm -rf /var/lib/apt/lists/*

RUN useradd -m -u 1000 -s /bin/bash apiary

COPY --from=builder /build/target/release/apiary /usr/local/bin/apiary
COPY --from=builder /build/target/wheels/*.whl /tmp/
RUN pip3 install --no-cache-dir /tmp/*.whl && rm -f /tmp/*.whl
RUN pip3 install --no-cache-dir pyarrow pandas

COPY scripts/apiary-node.py /usr/local/bin/apiary-node.py
RUN chmod +x /usr/local/bin/apiary-node.py

USER apiary
WORKDIR /home/apiary
RUN mkdir -p /home/apiary/data /home/apiary/cache

EXPOSE 8080
CMD ["python3", "/usr/local/bin/apiary-node.py"]

Build the image:

docker build -t apiary:latest .

# For Raspberry Pi (ARM64)
docker build --platform linux/arm64 -t apiary:latest .

# Multi-arch (requires buildx)
docker buildx build --platform linux/amd64,linux/arm64 -t apiary:latest .

Run a Single Container

# Solo mode with local storage
docker run -d --name apiary \
-v apiary-data:/home/apiary/data \
apiary:latest

# With MinIO storage
docker run -d --name apiary \
-e AWS_ACCESS_KEY_ID=minioadmin \
-e AWS_SECRET_ACCESS_KEY=minioadmin \
-e AWS_ENDPOINT_URL=http://minio:9000 \
-e APIARY_STORAGE_URL=s3://apiary/data \
-e APIARY_NAME=production \
apiary:latest

Multi-Node with Docker Compose

Create a docker-compose.yml:

services:
minio:
image: minio/minio:latest
container_name: apiary-minio
command: server /data --console-address ":9001"
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
volumes:
- minio-data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 10s
retries: 3

minio-setup:
image: minio/mc:latest
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
/usr/bin/mc config host add myminio http://minio:9000 $${MINIO_ROOT_USER:-minioadmin} $${MINIO_ROOT_PASSWORD:-minioadmin};
/usr/bin/mc mb myminio/apiary --ignore-existing;
exit 0;
"
restart: "no"

apiary-node:
image: ${APIARY_IMAGE:-apiary:latest}
depends_on:
minio-setup:
condition: service_completed_successfully
environment:
AWS_ACCESS_KEY_ID: ${MINIO_ROOT_USER:-minioadmin}
AWS_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD:-minioadmin}
AWS_ENDPOINT_URL: http://minio:9000
APIARY_STORAGE_URL: ${APIARY_STORAGE_URL:-s3://apiary/data}
APIARY_NAME: ${APIARY_NAME:-production}
RUST_LOG: ${RUST_LOG:-info}
volumes:
- apiary-cache:/home/apiary/cache
restart: unless-stopped

volumes:
minio-data:
apiary-cache:

Start the cluster:

docker compose up -d
note

The compose file uses a single apiary-node service that can be scaled. Nodes discover each other automatically through MinIO -- no additional configuration needed.

Scale Nodes

Add more nodes by scaling the service:

# Scale to 5 nodes
docker compose up -d --scale apiary-node=5

Nodes discover each other automatically through MinIO -- no additional configuration needed.

Verify the Cluster

Connect from a Python client:

import os
os.environ["AWS_ACCESS_KEY_ID"] = "minioadmin"
os.environ["AWS_SECRET_ACCESS_KEY"] = "minioadmin"
os.environ["AWS_ENDPOINT_URL"] = "http://localhost:9000"

from apiary import Apiary
ap = Apiary("production", storage="s3://apiary/data")
ap.start()

swarm = ap.swarm_status()
print(f"Total bees: {swarm['total_bees']}, Idle: {swarm['total_idle_bees']}")
for node in swarm['nodes']:
print(f" {node['node_id']}: {node['state']}")

ap.shutdown()
tip

For Raspberry Pi-specific Docker Compose profiles with resource constraints matched to each Pi model, see Pi Deploy Profiles.

Troubleshooting

ProblemSolution
Containers can't reach MinIOCheck that all containers are on the same Docker network
MinIO bucket not createdCheck minio-init container logs: docker compose logs minio-init
Nodes not discovering each otherVerify all nodes use the same APIARY_STORAGE value
Out of memoryLimit container memory with deploy.resources.limits.memory