Docker Compose Deployment
The fastest way to self-host Sentro. Runs the entire platform on a single machine using Docker Compose, with an automatic-HTTPS reverse proxy in front.
Prerequisites
- Docker 24+ (or Podman 4+)
- Docker Compose v2+
- 2 CPU cores, 4 GB RAM minimum
- An x86-64 Linux server (Ubuntu 22.04+, Debian 12+, or any Docker-compatible OS). ARM hosts are not supported — the images are published for x86-64 only.
Quick Start
Option 1: Automated install
curl -fsSL https://sentro.dev/install.sh | bash
Or, without the short domain:
bash <(curl -fsSL https://raw.githubusercontent.com/sentro-dev/self-hosted/main/install.sh)
This will:
- Check system requirements
- Download the deployment bundle
- Generate secure passwords and secrets
- Start all services
- Print your admin credentials
Everything is installed into ~/sentro by default. Use --dir /opt/sentro to install elsewhere, and --no-start to generate the configuration without booting the stack.
Option 2: Manual setup
1. Download the deployment files:
mkdir -p ~/sentro && cd ~/sentro
BASE=https://raw.githubusercontent.com/sentro-dev/self-hosted/main
curl -fsSL $BASE/docker-compose.yml -o docker-compose.yml
curl -fsSL $BASE/Caddyfile -o Caddyfile
curl -fsSL $BASE/.env.example -o .env.example
curl -fsSL $BASE/prometheus.yml -o prometheus.yml
curl -fsSL $BASE/server.mjs -o server.mjs
2. Configure your environment:
cp .env.example .env
Then fill in every value marked REQUIRED:
# Generate the secrets
openssl rand -hex 32 # AUTH_SECRET
openssl rand -hex 32 # ENCRYPTION_KEY (must be exactly 64 hex characters)
openssl rand -hex 32 # ADMIN_SESSION_SECRET
openssl rand -hex 16 # POSTGRES_PASSWORD
openssl rand -hex 16 # REDIS_PASSWORD
Also set your domains, and keep AUTH_URL in sync with DOMAIN_APP:
DOMAIN_APP=app.yourdomain.com
DOMAIN_ADMIN=admin.yourdomain.com
DOMAIN_API=api.yourdomain.com
AUTH_URL=https://app.yourdomain.com
AUTH_URL must be the exact URL your users open the dashboard on, including the scheme. If it disagrees with DOMAIN_APP, sign-in redirects will break.
3. Start the services:
docker compose up -d
4. Wait for services to be ready (about 30 seconds):
docker compose ps # all services should report "healthy"
docker compose logs -f # follow logs
5. Access your instance:
Once DNS for your domains points at the server, the bundled Caddy proxy obtains HTTPS certificates automatically:
| Service | URL |
|---|---|
| Dashboard | https://app.yourdomain.com |
| Admin Panel | https://admin.yourdomain.com |
| REST API | https://api.yourdomain.com |
Every service is also published on the loopback interface, which is handy before DNS is ready and for local evaluation:
| Service | URL |
|---|---|
| Dashboard | http://localhost:3001 |
| Admin Panel | http://localhost:3002 |
| REST API | http://localhost:4000 |
| API health | http://localhost:4000/health |
| App health | http://localhost:3001/api/health |
Services
The default stack starts these containers:
| Container | Image | Port | Purpose |
|---|---|---|---|
sentro-caddy | caddy:2-alpine | 80, 443 | Reverse proxy + automatic HTTPS |
sentro-app | sentro-app | 3001 | User dashboard |
sentro-admin | sentro-admin | 3002 | Admin panel |
sentro-api | sentro-api | 4000 | REST API |
sentro-worker | sentro-worker | 9464 (metrics) | Monitor check engine |
sentro-postgres | postgres:16-alpine | 5432 | Database |
sentro-redis | redis:7-alpine | 6379 | Job queue |
sentro-migrate | sentro-app | — | Runs DB migrations (exits after) |
Changing host ports
If a port is already taken on your machine, override it in .env — nothing else needs to change:
CADDY_HTTP_PORT=8080
CADDY_HTTPS_PORT=8443
APP_HOST_PORT=13001
ADMIN_HOST_PORT=13002
API_HOST_PORT=14000
POSTGRES_HOST_PORT=15432
REDIS_HOST_PORT=16379
Optional Profiles
Profiles are additive — combine them freely.
Documentation portal
docker compose --profile docs up -d
Serves this documentation site locally on http://localhost:3003, and on DOMAIN_DOCS if you set one.
Sandbox (mock endpoints + email capture)
docker compose --profile sandbox up -d
Adds a mock HTTP target you can point test monitors at, plus MailHog so outgoing email is captured instead of delivered:
- Mock endpoints: http://localhost:1080/healthy,
/down,/slow,/flapping,/random-errors - MailHog UI: http://localhost:8025
To route real application email through MailHog, set SMTP_HOST=mailhog and SMTP_PORT=1025 in .env.
For production, configure a real SMTP relay instead:
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASS=your-sendgrid-api-key
SMTP_FROM=alerts@yourdomain.com
Monitoring stack (Prometheus + Grafana)
docker compose --profile monitoring up -d
- Grafana: http://localhost:3030 (user
admin, password fromGRAFANA_ADMIN_PASSWORD) - Prometheus: http://localhost:9090
Prometheus is pre-configured to scrape the worker's metrics endpoint — no scrape config to write.
HTTPS and Reverse Proxy
The stack ships with Caddy and handles certificates for you. Point the DNS records for DOMAIN_APP, DOMAIN_ADMIN, and DOMAIN_API at the server, make sure ports 80 and 443 reach it, and certificates are issued on first request.
Using your own proxy instead
If you already run nginx, Traefik, or a cloud load balancer, disable Caddy and proxy to the loopback ports directly:
docker compose up -d --scale caddy=0
Then forward each hostname to the matching local port:
server {
listen 443 ssl http2;
server_name app.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Repeat for admin.yourdomain.com → 127.0.0.1:3002 and api.yourdomain.com → 127.0.0.1:4000.
Whatever proxy you use, the hostname users type must appear in DOMAIN_APP (or APP_HOSTNAMES). Requests arriving on an unrecognised hostname are treated as custom status-page domains and will return "Status page not found".
Data & Backups
Back up PostgreSQL
# Create backup
docker exec sentro-postgres pg_dump -U sentro sentro > backup-$(date +%Y%m%d).sql
# Restore from backup
cat backup-20260101.sql | docker exec -i sentro-postgres psql -U sentro sentro
Back up volumes
# Stop services first
docker compose down
# Back up the data volumes
tar czf sentro-backup-$(date +%Y%m%d).tar.gz \
$(docker volume inspect sentro_postgres_data --format '{{ .Mountpoint }}') \
$(docker volume inspect sentro_redis_data --format '{{ .Mountpoint }}')
# Restart
docker compose up -d
Back up your .env too, and keep it somewhere safe. ENCRYPTION_KEY cannot be regenerated — losing it makes every encrypted field in the database permanently unreadable.
Automated backups
# /etc/cron.d/sentro-backup
0 3 * * * root cd /root/sentro && docker exec sentro-postgres pg_dump -U sentro sentro | gzip > /backups/sentro-$(date +\%Y\%m\%d).sql.gz
Troubleshooting
Services won't start
docker compose logs -f # check logs
docker stats # check resource usage
docker compose config # verify .env is loaded and valid
A port conflict on 80, 443, 3001, 3002, 4000, 5432, or 6379 is the most common cause — see Changing host ports.
Database connection issues
docker exec sentro-postgres pg_isready -U sentro
docker compose logs migrate
"Status page not found" when opening the dashboard
The hostname you used is not recognised as an app hostname. Set DOMAIN_APP to that hostname (or add it to APP_HOSTNAMES), keep AUTH_URL in sync, and restart.
Worker not processing checks
docker compose logs -f worker
docker exec sentro-redis redis-cli -a YOUR_REDIS_PASSWORD ping
Reset everything
# Stop and remove all containers + volumes (DATA LOSS!)
docker compose down -v
# Start fresh
docker compose up -d