Skip to content
Feeblo Docs
Esc
navigateopen⌘Jpreview
On this page

Deploy with Docker

Run the full Feeblo stack with docker compose: what each service does, first boot, and the upgrade path.

The docker-compose.yml at the repo root defines a complete single-host deployment: API server, dashboard, PostgreSQL, and Redis. Both app containers come from prebuilt GHCR images, so nothing builds locally. The server container runs database migrations before it starts listening, which turns upgrades into a pull-and-restart.

Prerequisites

  • Docker Engine and Docker Compose v2. Check yours with docker compose version.
  • DNS records for three hosts: your dashboard URL, your API URL, and a wildcard record (*.example.com) covering board subdomains.
  • SMTP credentials. New accounts must click a verification link by default, so mail has to flow before anyone can register.
  • A way to generate random keys (openssl works).

The services

Service Image Host port (default)
pg pgvector/pgvector:pg17 5432
redis redis:7-alpine 6379
server ghcr.io/g3root/feeblo-server:$IMAGE_TAG 8080
web ghcr.io/g3root/feeblo-web:$IMAGE_TAG 4321

Details worth knowing before you boot:

  • Healthchecks gate startup. Postgres reports ready via pg_isready, Redis via redis-cli ping. The server starts only after both pass; the web container waits for the server.
  • Migrations run on every server start. The container’s start command runs node ./migrate/index.js before launching the API, so schema changes apply themselves.
  • Data lives in named volumes called postgres and redis. Deleting a volume deletes your data.
  • The server exposes /health, returning {"status":"ok","release":"..."} once it listens.

First boot

Create your env file

Create .env next to docker-compose.yml:

# Required. Compose refuses to start without it.
# Generate with: openssl rand -hex 32
AUTH_ENCRYPTION_KEY=

# Public origins. These must match reality or sign-in breaks.
APP_URL=https://app.example.com     # dashboard
API_URL=https://api.example.com     # HTTP API
APP_ROOT_DOMAIN=example.com         # boards live on <name>.example.com

# Outgoing email. See the Email page for every option.
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=your-username
SMTP_PASSWORD=your-password
SMTP_FROM_ADDRESS=noreply@example.com

You rarely need to set AUTH_TRUSTED_ORIGINS: compose derives a default of $APP_URL,$API_URL,*.$APP_ROOT_DOMAIN, and the wildcard covers every board subdomain. Add an override only when browsers call the API from extra origins; scheme-less patterns match both http and https hosts.

Start the stack

docker compose up -d

Postgres and Redis turn healthy first, the server runs migrations and comes up, then the dashboard follows. Watch progress with:

docker compose ps

Verify the API is alive

curl https://api.example.com/health

A JSON body with "status":"ok" means migrations finished and the server listens.

Create your account

Open APP_URL in a browser. Email and password sign-up is enabled by default (AUTH_SIGN_UP_ENABLED), so register, click the verification link that arrives by email, and sign in. GitHub and Google sign-in switch on as soon as you configure their client credentials (see OAuth).

Upgrades

New app images arrive continuously; pin IMAGE_TAG to a specific GHCR tag instead of latest so you decide when to move. To upgrade:

# Back up first
docker compose exec pg pg_dump -U feeblo feeblo > feeblo-backup.sql

# Pull new images and recreate containers; migrations run on server start
docker compose pull
docker compose up -d

To roll back, point IMAGE_TAG at the previous tag and run docker compose up -d again. If the newer release migrated the schema past what the older image understands, restore your backup into the postgres volume first.

Changing embedding dimensions

Post embeddings default to OpenAI text-embedding-3-small at 1536 dimensions. When you switch to a model with a different vector size, reconfigure the database column from the published server image before restarting it:

docker compose run --rm server \
  node ./migrate/configure-embeddings.js \
  --dimensions 768 \
  --clear-existing

The command rebuilds the vector index for the new size. With --clear-existing it drops vectors that no longer fit; without it, the command fails safely instead of losing data.

Next steps

Was this page helpful?