Production deployment with Docker Compose
This page deploys the IMCore Basic edition from zero onto a public VPS: Docker Compose brings up PostgreSQL + Redis + the server, while Caddy terminates TLS with automatically issued certificates and proxies WebSocket. About 15 minutes end to end.
Just want to kick the tires locally? Use the one-command setup in the Quickstart (placeholder secrets — never expose it publicly). Compose, Kubernetes and systemd (see the release README) are the three supported deployment forms — pick one per environment.
Prerequisites
- A Linux server (2 vCPU / 4 GB minimum; measured capacity of a 4C8G single instance is in Self-hosting a chat server)
- A domain name with an A record pointing at the server (examples use
im.example.com— replace with yours) - Docker Engine 24+ and Docker Compose v2.23+ (check with
docker compose version) - Ports 80 / 443 open (80 for certificate issuance, 443 for traffic)
1. Directory and secrets
mkdir -p /opt/imcore && cd /opt/imcore
Generate two secrets and keep them (both are used in the next two steps):
openssl rand -base64 24 # POSTGRES_PASSWORD
openssl rand -base64 32 # JWT_SECRET (for minting client tokens)
2. Server configuration — app.yaml
Create /opt/imcore/app.yaml. Compared with the local demo config, exactly three things change: placeholder secrets become the generated values, URLs switch to your domain (https/wss), and demo mode is disabled:
app:
logFile: log/gin.log
httpPort: 8080
standalone_websocket_enabled: false
webSocketPort: 8089
rpcPort: 9001
httpUrl: https://im.example.com
webSocketUrl: wss://im.example.com/acc
autoOpenBrowser: false
redis:
addr: "redis:6379"
username: ""
password: ""
DB: 0
poolSize: 50
minIdleConns: 10
database:
driver: "postgres"
postgres:
dsn: "postgres://imcore:<POSTGRES_PASSWORD>@postgres:5432/imcore?sslmode=disable&connect_timeout=5"
im:
dsn: "postgres://imcore:<POSTGRES_PASSWORD>@postgres:5432/imcore?sslmode=disable&connect_timeout=5"
auth:
jwt_secret: "<JWT_SECRET>"
jwt_issuer: "imcore-prod"
jwt_audience: "gowebsocket-client"
jwt_required: true
allow_empty_origin: false
demo:
product_mode: false
guest_token_enabled: false
seed:
enabled: false
broker:
type: ""
rabbitmq:
enabled: false
Notes:
- Replace
<POSTGRES_PASSWORD>in bothpostgres.dsnentries with the database password from step 1. demo.*is fully disabled — guest login and the seeded demo group belong to the local experience config only.- The Basic edition is single-instance and needs no broker; clustered topologies (Pro and up) are covered in the last section.
- Paid editions additionally place the license file on the server and point
license.fileat it (not needed for Basic).
3. docker-compose.yml
Create /opt/imcore/docker-compose.yml:
name: imcore
services:
postgres:
image: pgvector/pgvector:pg16
environment:
POSTGRES_USER: imcore
POSTGRES_PASSWORD: <POSTGRES_PASSWORD>
POSTGRES_DB: imcore
volumes:
- pgdata:/var/lib/postgresql/data
configs:
- source: initdb_vector
target: /docker-entrypoint-initdb.d/01-vector.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U imcore -d imcore"]
interval: 5s
timeout: 5s
retries: 12
restart: unless-stopped
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 12
restart: unless-stopped
# One-shot: applies pending SQL migrations, then exits.
# Re-runs automatically on `docker compose up -d` after an upgrade.
migrate:
image: ${IMCORE_IMAGE:-crpi-zegbek7xpkekqods.cn-hangzhou.personal.cr.aliyuncs.im/balala/imcore-basic:latest}
environment:
APP_BIN: migrate
configs:
- source: app_yaml
target: /app/config/app.yaml
depends_on:
postgres:
condition: service_healthy
restart: "no"
imcore:
image: ${IMCORE_IMAGE:-crpi-zegbek7xpkekqods.cn-hangzhou.personal.cr.aliyuncs.im/balala/imcore-basic:latest}
configs:
- source: app_yaml
target: /app/config/app.yaml
depends_on:
migrate:
condition: service_completed_successfully
redis:
condition: service_healthy
restart: unless-stopped
caddy:
image: caddy:2-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
depends_on:
- imcore
restart: unless-stopped
configs:
app_yaml:
file: ./app.yaml
initdb_vector:
content: |
CREATE EXTENSION IF NOT EXISTS vector;
volumes:
pgdata:
caddy_data:
caddy_config:
Notes:
- The default image comes from Aliyun ACR (stable pulls inside China). For servers elsewhere use Docker Hub:
IMCORE_IMAGE=balalaim/imcore-basic:latest docker compose up -d. imcorepublishes no ports — only Caddy reaches it over the compose network; the same goes for PostgreSQL and Redis.initdb_vectorenables the pgvector extension at first database initialization (required by the vector tables of the CS edition’s RAG features); it runs only on an empty data dir.- Air-gapped networks:
docker pullall images on a connected machine,docker save, transfer,docker load.
4. Caddyfile (automatic HTTPS)
Create /opt/imcore/Caddyfile with your domain:
im.example.com {
encode gzip zstd
reverse_proxy imcore:8080
}
Caddy obtains and renews Let’s Encrypt certificates automatically; WebSocket upgrades pass through reverse_proxy natively, so /acc needs no extra configuration.
5. Start and verify
docker compose up -d
docker compose ps # postgres/redis/imcore/caddy Up, migrate Exited(0)
docker compose logs migrate # confirm all migrations applied
Open https://im.example.com in a browser — if the site loads, the server is up. Clients follow the client integration guide: your backend mints JWTs and connects to wss://im.example.com/acc.
Upgrades and backups
# Upgrade: pull the new image and re-up (migrate applies the new version's migrations)
docker compose pull
docker compose up -d
# Database backup (daily cron recommended)
docker compose exec postgres pg_dump -U imcore imcore > imcore-$(date +%F).sql
Rollback: switch back to the previous image tag and up -d again (see the release README for migration compatibility policy).
When you need more components
| Need | Add |
|---|---|
| Horizontal scaling (Pro and up) | A RabbitMQ / NATS / Redis Streams broker + persist_consumer; all replicas share one Redis and one broker |
| Orchestration, HPA, rolling upgrades | Kubernetes (Helm) |
| AI support / RAG knowledge base | CS edition image + PostgreSQL (pgvector — already in place here) |
| Preference for systemd on a single host | The systemd path in the release README |
For selection background and capacity numbers see Self-hosting a chat server; for head-to-head comparisons against SaaS and open-source options see the comparison pages.