Docker Deployment
This guide covers deploying Halo Server with plain Docker (docker run / docker compose).
1. Build the image
Clone the halo-server repo and build from its Dockerfile. The repo is self-contained and offline-buildable — the app bundle, Chromium/Electron system libs, and fonts all ship with it, so the build needs no network (except to pull the base image).
git clone <halo-server repo URL>
cd halo-server
docker build -t halo-server:latest .Base image
The Dockerfile is based on the public image python:3.12-slim (Debian 12 bookworm slim). To use an equivalent base image reachable from your own environment, just change the Dockerfile's FROM.
2. Run the container
Mount a local directory as the persistent data volume, and inject the token and LLM config via environment variables.
docker run -d \
--name halo-server \
-p 8080:8080 \
-v /your/data:/data \
-e HALO_SERVER_MODE=1 \
-e HALO_DATA_DIR=/data \
-e HALO_REMOTE_PASSWORD=replace-with-strong-random-token \
-e HALO_AI_PROVIDER=openai \
-e HALO_AI_API_URL=https://ai-gateway.acme.intra/v1 \
-e HALO_AI_API_KEY=sk-xxxxxxxxxxxxxxxx \
-e HALO_AI_MODEL=gpt-4o \
halo-server:latestKey points:
-p 8080:8080: maps the container's8080(thePORTdefault) to the host.-v /your/data:/data+-e HALO_DATA_DIR=/data: writes data to the mounted volume so it survives container recreation.HALO_REMOTE_PASSWORD: for any externally reachable deployment, change to a strong random token (openssl rand -hex 32).- The four LLM vars point at your own enterprise gateway; see Configuration.
Persistence
Without a -v mount and HALO_DATA_DIR, data is written to the in-container default /tmp/.halo and is lost when the container is removed.
Resources
Halo Server runs a full Electron (Chromium) main process with meaningful memory needs. Allow ≥ 4–8 GB of memory for the container, or it may OOM. You can use --memory / --cpus to constrain it, ensuring the host has enough headroom.
3. docker compose
services:
halo-server:
image: halo-server:latest
# or build from the repo directly:
# build: .
container_name: halo-server
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- ./data:/data
environment:
HALO_SERVER_MODE: "1"
HALO_DATA_DIR: /data
HALO_REMOTE_PASSWORD: replace-with-strong-random-token
HALO_AI_PROVIDER: openai
HALO_AI_API_URL: https://ai-gateway.acme.intra/v1
HALO_AI_API_KEY: sk-xxxxxxxxxxxxxxxx
HALO_AI_MODEL: gpt-4o
# Electron/Chromium has meaningful memory needs
mem_limit: 8gStart:
docker compose up -d4. Verify
Liveness probe (public endpoint, no token needed):
curl http://localhost:8080/api/remote/status
# => {"success":true,"data":{"active":true,"clients":0,"version":"1.0.0"}}Validate the token:
curl -X POST http://localhost:8080/api/remote/login \
-H "Content-Type: application/json" \
-d '{"token":"<HALO_REMOTE_PASSWORD>"}'
# => {"success":true}View logs (on startup the container prints any still-missing shared libs once, to help diagnose base-image differences):
docker logs -f halo-serverSingle-instance constraint
As with serverless deployment, Halo Server is a stateful single instance (SQLite + files + in-memory sessions). Do not run multiple container instances against the same data volume — multiple writers will corrupt SQLite. For higher throughput, scale vertically (give the single container more CPU / memory). See the scaling notes in Serverless deployment.