Skip to content

Deployment

The simplest live deployment is a single Linux host running Docker Compose. The production compose stack starts:

  • traefik — reverse proxy, terminates TLS via Let’s Encrypt.
  • db — Postgres 18 with pgvector.
  • app — the Next.js app, pulled from GHCR.

The app container applies committed database migrations before starting Next.js.

  • A Linux host with Docker Engine and Docker Compose v2 installed.
  • A DNS A record for your app host, for example offers.example.com, pointing to the host’s public IP.
  • Inbound TCP 80 and 443 open in the host firewall.
  • Access to the public GHCR image ghcr.io/solivio-ai/solivio-app.
  • An OpenAI API key for AI-backed catalog import, offer generation, semantic search, and offer chat.

The production image is built from apps/solivio/Dockerfile:

  • ghcr.io/solivio-ai/solivio-app — Next.js standalone runtime that ships with the committed migrations and applies them on container startup.

Both are declared in docker-compose.build.yml. CI (.github/workflows/build-image.yml) runs on every push to main:

Terminal window
IMAGE_TAG=$GITHUB_SHA docker compose -f docker-compose.build.yml build --pull
docker compose -f docker-compose.build.yml push

Each push tags the image with :latest and :<commit-sha>. Pin IMAGE_TAG=<sha> in /opt/solivio/.env for reproducible deploys; leave on latest to always run the newest build.

To build locally:

Terminal window
docker compose -f docker-compose.build.yml build

The production stack reads secrets and host-specific values from .env next to docker-compose.prod.yml.

Important values:

VariablePurpose
APP_HOSTPublic hostname routed by Traefik.
LETSENCRYPT_EMAILEmail used for Let’s Encrypt certificate registration.
IMAGE_TAGlatest for fast iteration, or a commit SHA for reproducible deploys.
POSTGRES_PASSWORDDatabase password used by the db service.
DATABASE_URLInternal app database URL, usually postgresql://solivio:<password>@db:5432/solivio.
OPENAI_API_KEYEnables AI-backed product import, matching, offer generation, and chat.
OPENAI_MODEL_*Optional per-role model overrides (see apps/solivio/src/server/agents/modelConfig.ts).
EMBEDDING_MODELOptional override for product embedding model. Defaults to text-embedding-3-large.
BETTER_AUTH_URLPublic app URL, for example https://offers.example.com.
BETTER_AUTH_SECRETAuth signing secret. Generate with openssl rand -base64 32.
AUTH_SIGNUP_ENABLEDKeep true for first setup; set false after creating initial users in shared environments.
Terminal window
sudo mkdir -p /opt/solivio
sudo chown $USER:$USER /opt/solivio
cd /opt/solivio
git clone --depth 1 https://github.com/solivio-ai/Solivio.git .
cp .env.production.example .env
# Fill in: APP_HOST, LETSENCRYPT_EMAIL, POSTGRES_PASSWORD,
# DATABASE_URL, BETTER_AUTH_URL, BETTER_AUTH_SECRET, and OPENAI_API_KEY.

Generate secrets on the host:

Terminal window
openssl rand -base64 32

Paste the value into BETTER_AUTH_SECRET.

Terminal window
cd /opt/solivio
git pull
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d

up -d starts the database, waits for it to become healthy, then starts the app. The app container runs pending migrations before Next.js starts. Traefik requests the TLS certificate from Let’s Encrypt the first time your configured host is reached.

Open https://<APP_HOST>, create the first account, then consider setting AUTH_SIGNUP_ENABLED=false and running docker compose -f docker-compose.prod.yml up -d again.

Terminal window
docker compose -f docker-compose.prod.yml ps
curl -fsS https://<APP_HOST>/api/health

All services should be running.

Pin IMAGE_TAG to the last known-good SHA in /opt/solivio/.env, then:

Terminal window
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d
Terminal window
docker compose -f docker-compose.prod.yml logs -f app
docker compose -f docker-compose.prod.yml logs -f traefik
docker compose -f docker-compose.prod.yml exec db psql -U solivio -d solivio
docker compose -f docker-compose.prod.yml restart app

After the app is reachable:

  1. Create an admin or evaluation account.
  2. Import a small CSV catalog from Catalog Upload.
  3. Generate a draft from New Offer using a real customer request.
  4. Review unmatched items and pricing in the offer review screen.
  5. Accept the draft and download the generated PDF.
  6. Disable public signup if the environment is shared.

Production uses committed Drizzle migrations. For schema changes:

Terminal window
yarn db:generate
yarn db:migrate

Review generated SQL before merging to main; the app container applies the same migrations on startup after the updated image is pulled.

/opt/solivio/.env holds every secret used by the stack and is not committed. To rotate, edit the file and run docker compose -f docker-compose.prod.yml up -d.