Skip to content

Docker Compose

Standard production deployment: pull the prebuilt GHCR image and run it with Docker Compose. No Node.js or Git checkout is required on the server.

For a friendlier wrapper around the same Compose flow, see npm CLI. For host firewall, backups, and day-two VPS operations, see VPS.

Prerequisites

Deploy

  1. Copy the production Compose template and environment example from this repository (packages/team-hub/deploy/) or from an installed npm package (deploy/ directory inside @harborclient/team-hub):
bash
curl -fsSLO https://raw.githubusercontent.com/harborclient/harborclient/main/packages/team-hub/deploy/compose.yaml
curl -fsSLO https://raw.githubusercontent.com/harborclient/harborclient/main/packages/team-hub/deploy/.env.example
cp .env.example .env
  1. Edit .env and set at least:
VariablePurpose
APP_VERSIONImage tag (0.8.0, latest, …)
HOST_PORTHost port mapped to container 8080 (default 8080)
TEAM_HUB_DB_PASSWORDStrong password for bundled Postgres
  1. Pull and start:
bash
docker compose pull
docker compose up -d --remove-orphans
  1. Verify health:
bash
curl -s http://127.0.0.1:8080/health

Expect JSON like {"status":"ok","version":"0.8.0"}. First boot can take 30–60 seconds while Postgres initializes and migrations run.

Image authentication

If the GHCR package is private, authenticate before pulling:

bash
echo "$GITHUB_TOKEN" | docker login ghcr.io -u USERNAME --password-stdin

Public packages do not need login.

Updates and rollbacks

bash
# Update to a newer tag (edit APP_VERSION in .env if needed)
docker compose pull
docker compose up -d --remove-orphans

# Roll back: set APP_VERSION to an older release, then pull/up again

The entrypoint runs migrations on each start, so schema updates apply automatically.

Logs and lifecycle

bash
docker compose logs --follow
docker compose logs --tail 200
docker compose stop          # keep containers and volumes
docker compose down          # remove containers; keep named volumes
docker compose down --volumes  # also delete Postgres data — destructive

Direct docker run

Compose is recommended. Equivalent one-off run:

bash
docker pull ghcr.io/harborclient/team-hub:0.8.0

docker run -d \
  --name team-hub \
  --restart unless-stopped \
  --env-file .env \
  -p 8080:8080 \
  -v team-hub-pgdata:/var/lib/postgresql/data \
  ghcr.io/harborclient/team-hub:0.8.0

Local smoke test

Build from the monorepo root when testing Dockerfile changes locally:

bash
docker build -f packages/team-hub/Dockerfile -t team-hub:local .

docker run --rm -p 8080:8080 \
  -e TEAM_HUB_DB_PASSWORD=harbor \
  team-hub:local

Mount a volume if you need Postgres data to survive restarts:

bash
docker run --rm -p 8080:8080 \
  -v team-hub-pgdata:/var/lib/postgresql/data \
  -e TEAM_HUB_DB_PASSWORD=harbor \
  team-hub:local

Using the CLI in the container

Administration commands (user, migrate, collection, and so on) run through the same CLI as a host install. In the Docker image:

  • The config file is generated at /etc/team-hub/server.yaml.
  • The team-hub binary is not on PATH.
  • The CLI does not read TEAM_HUB_CONFIG — pass -c explicitly, before the subcommand.
bash
docker exec -it team-hub \
  node /app/dist/cli.js -c /etc/team-hub/server.yaml user list

Create an admin user

bash
docker exec -it team-hub \
  node /app/dist/cli.js -c /etc/team-hub/server.yaml user create --name ops --role admin

Copy the one-time hbk_… token immediately; it is not shown again.

Create a desktop user

bash
docker exec -it team-hub \
  node /app/dist/cli.js -c /etc/team-hub/server.yaml user create --name alice --role user \
  --collection-access '*' --environment-access '*'

Reload or restart after config edits

SectionLive reload?Action
db, redis, llm, pluginsYesSIGHUP or POST /admin/config/reload
logging, server.host / server.portNoRestart Team Hub process
bash
# Live reload (admin token required)
docker exec team-hub curl -s -X POST http://127.0.0.1:8787/admin/config/reload \
  -H "Authorization: Bearer hbk_your_admin_token_here"

# Restart Team Hub only (Nginx/Postgres/Redis stay up)
docker exec team-hub /docker/restart-team-hub.sh

To persist config across container recreation, bind-mount a host server.yaml to /etc/team-hub/server.yaml.

See CLI and Configuration for full reference.

Documentation index

The Docker image bundles HarborClient documentation for hub-native search_docs. During docker build, the Dockerfile downloads docsSearchIndex.json (override with build arg DOCS_INDEX_URL) and copies it to /app/data/docsSearchIndex.json.

For self-hosted deployments without rebuilding the image, mount an updated index file and point docs.searchIndexPath in server.yaml at the mount path.

Environment variable reference

VariableDefaultDescription
PORT8080Nginx listen port (some platforms inject this at runtime)
NGINX_SERVER_NAME_Nginx server_name (catch-all _; set to your hostname for named vhosts)
TEAM_HUB_PORT8787Internal Team Hub port
TEAM_HUB_HOST127.0.0.1Team Hub bind address
TEAM_HUB_CONFIG/etc/team-hub/server.yamlConfig file path (generated on first boot if missing)
TEAM_HUB_FORCE_CONFIG_GENERATEfalseWhen true, overwrite an existing config from env vars
TEAM_HUB_START_POSTGREStrueStart bundled Postgres
TEAM_HUB_START_REDIStrueStart bundled Redis
TEAM_HUB_DB_DRIVERpostgrespostgres, mysql, or firestore
TEAM_HUB_DB_HOST127.0.0.1Database host
TEAM_HUB_DB_PORT5432Database port
TEAM_HUB_DB_USERharborDatabase user
TEAM_HUB_DB_PASSWORDharborDatabase password
TEAM_HUB_DB_DATABASEharborDatabase name
TEAM_HUB_REDIS_HOST127.0.0.1Redis host
TEAM_HUB_REDIS_PORT6379Redis port
TEAM_HUB_LOGGING_LEVELinfoLog level (debug, info, warn, error)
TEAM_HUB_LOGGING_FILE/var/log/team-hub/team-hub.logLog file path
TEAM_HUB_LOGGING_CONSOLEtrueWrite logs to the terminal

Logging env vars are applied at process startup. Restart the container after changing them.

Troubleshooting

Container exits during startup

Check docker compose logs or docker logs team-hub. Bundled Postgres + Redis + Node need at least 2 GiB RAM. Ensure PGDATA (/var/lib/postgresql/data) is writable.

GET /health fails or connection refused

  • Confirm the service listens on the mapped host port (default 8080).
  • Wait for startup: migrations and Postgres init can take 30–60 seconds on cold start.

Protected API routes return 503

Redis is required for auth throttling. Verify Redis is running (bundled) or reachable (external Redis). See Authentication.

When redis.noticeEventsPubSub: true is set for multi-instance notice SSE fan-out, the notice stream route (GET /notices/stream) also requires a healthy Redis connection and returns 503 if pub/sub is unavailable.

Notice SSE (GET /notices/stream) disconnects behind a reverse proxy

Long-lived Server-Sent Events need proxy buffering disabled and extended read timeouts. The bundled Docker image configures Nginx for /notices/stream automatically (proxy_buffering off, proxy_read_timeout 1h, HTTP/1.1).

Self-hosted reverse proxies must apply equivalent settings on the SSE path only — normal REST routes can keep default buffering. HarborClient desktop clients fall back to REST polling when the stream is unavailable.

Config file not found

The CLI defaults to server.yaml in the current directory. In the container pass /etc/team-hub/server.yaml with -c before the subcommand:

bash
node /app/dist/cli.js -c /etc/team-hub/server.yaml user list