Home/Docs/Self-Hosted Deployment

Self-Hosted Deployment Guide

Deploy Systemi on your own infrastructure using Docker. Your data never leaves your network.

License. Use of the self-hosted Software is subject to our End User License Agreement (EULA). © 2024–2026 MDG LLC. Systemi™ is a trademark of MDG LLC.

Overview

Systemi can be deployed on your own infrastructure using Docker. This ensures all data remains within your network. The application is distributed as a compiled Docker image — no source code is included.

Prerequisites

  • Docker and Docker Compose installed
  • A machine with at least 2 CPU cores and 2 GB RAM
  • A signed contract and registry credentials (username + token to pull the image)
  • A valid Systemi license key (provided with your contract)
  • Network access to your Jira, GitHub, and/or Slack APIs

The Docker image is private. You need registry credentials (provided after contract signing) to pull it. Access is controlled by both the registry token and your license key.

Don't have a contract or credentials yet? Contact sales to get started.

Quick Start

1

Download the deployment files

Create a directory and download the configuration files:

mkdir systemi && cd systemi

# Download docker-compose.yml and .env template
curl -O https://getsystemi.com/deploy/docker-compose.yml
curl -O https://getsystemi.com/deploy/.env.example
cp .env.example .env

Or create the files manually — see the Docker Compose reference below.

2

Configure environment

Edit .env with your settings:

.env
# Required
POSTGRES_PASSWORD=<generate-a-strong-password>
DATABASE_URL=postgresql://systemi:<your-password>@db:5432/systemi
NEXTAUTH_URL=https://systemi.yourdomain.com
SYSTEMI_LICENSE_KEY=<your-license-key>
ENCRYPTION_KEY=<base64-encoded-32-byte-key>

# Registry (provided with your contract)
GHCR_REGISTRY_USER=<your-registry-username>
GHCR_REGISTRY_TOKEN=<your-registry-token>

# Admin account (created on first run)
SUPERADMIN_EMAIL=admin@yourdomain.com
SUPERADMIN_PASSWORD=<strong-password>
SUPERADMIN_NAME=Admin

# OAuth Integrations (optional — for one-click connect)
# ATLASSIAN_CLIENT_ID=<from-developer.atlassian.com>
# ATLASSIAN_CLIENT_SECRET=
# GITHUB_CLIENT_ID=<from-github.com/settings/developers>
# GITHUB_CLIENT_SECRET=
# SLACK_CLIENT_ID=<from-api.slack.com/apps>
# SLACK_CLIENT_SECRET=

# Optional — Email (Systemi SMTP is used by default)
# SMTP_HOST=smtp.yourdomain.com
# SMTP_PORT=465
# EMAIL=noreply@yourdomain.com
# PASSWORD=<smtp-password>

# Optional — AI (OpenAI or Google Gemini)
# OPENAI_API_KEY=<for-ai-powered-insights>
# GEMINI_API_KEY=<from-aistudio.google.com>  # and set AI_PROVIDER=gemini if both keys exist

# Optional — Slack message sync pacing (ms between API pages; eases rate limits)
# SLACK_API_MIN_INTERVAL_MS=600
# Large workspaces: Slack message sync checkpoints by channel (resumes across cron). Tune thread cap / daily cron in Admin → Integrations → Slack.

CRON_SECRET=<random-secret-for-cron-jobs>

Note: PostgreSQL is included in the Docker Compose setup — no external database needed. The DATABASE_URL is auto-configured from POSTGRES_PASSWORD. Email works out of the box using Systemi's built-in SMTP relay. AI features work without an API key — they fall back to pre-written insights. Use OPENAI_API_KEY or GEMINI_API_KEY (see AI_PROVIDER in env docs). OAuth variables are only needed for one-click connect; users can always connect integrations via manual API tokens.

3

Log in to the container registry

Authenticate using the credentials provided with your contract:

echo $GHCR_REGISTRY_TOKEN | docker login ghcr.io -u $GHCR_REGISTRY_USER --password-stdin

This is a one-time step; Docker caches the credentials.

4

Pull and start the application

docker compose pull
docker compose up -d

On first start, the app applies the database schema and creates the superadmin account from your SUPERADMIN_* env vars — no manual init step needed. On ARM64 (Apple Silicon, Graviton) you may see a platform warning; run with DOCKER_DEFAULT_PLATFORM=linux/amd64 docker compose up -d. The provided docker-compose.yml sets platform: linux/amd64 for the app.

5

Access the application

Open https://systemi.yourdomain.com (or http://localhost:3000 for local testing) and log in with the admin credentials you set in step 2.

Docker Compose Reference

Here is the full docker-compose.yml for reference. You can create this file manually if you prefer not to download it:

docker-compose.yml
version: "3.8"

services:
  app:
    platform: linux/amd64
    image: ghcr.io/mdgart/systemi:latest
    ports:
      - "3000:3000"
    depends_on:
      db:
        condition: service_healthy
    env_file:
      - .env
    environment:
      - DATABASE_URL=postgresql://systemi:${POSTGRES_PASSWORD}@db:5432/systemi
      - NODE_ENV=production
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: systemi
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: systemi
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U systemi"]
      interval: 5s
      timeout: 5s
      retries: 5
    restart: unless-stopped

volumes:
  pgdata:

Architecture

┌─────────────────────────────────────────┐
│             Your Network                │
│                                         │
│  ┌──────────┐     ┌──────────────────┐  │
│  │ Systemi  │────▶│   PostgreSQL     │  │
│  │   App    │     │   (your DB)      │  │
│  │ :3000    │     │   :5432          │  │
│  └────┬─────┘     └──────────────────┘  │
│       │                                 │
└───────┼─────────────────────────────────┘
        │ Outbound only (read-only APIs)
        ▼
   ┌────────────┐  ┌──────────┐  ┌───────┐
   │ Jira Cloud │  │  GitHub  │  │ Slack │
   └────────────┘  └──────────┘  └───────┘
  • All data is stored in your PostgreSQL instance
  • Outbound traffic is limited to read-only API calls to Jira, GitHub, and Slack
  • No data is sent to Systemi's servers
  • The Docker image contains only compiled production code (no source code)

Updating

When a new version is released, pull the latest image and restart. Your data is preserved in the PostgreSQL volume. Ensure you are logged in to the registry (docker login ghcr.io) if your session has expired.

# Pull the latest image
docker compose pull

# Restart with the new version
docker compose up -d

# Apply any database schema changes
docker compose exec app node node_modules/prisma/build/index.js db push

To pin a specific version instead of latest, edit your docker-compose.yml and change the image tag:

image: ghcr.io/mdgart/systemi:1.2.0

If the update doesn't take effect, Docker may be using a cached image. Remove it and pull again (no need to stop containers or reset the database). If you use a versioned tag, replace latest with your tag:

docker rmi ghcr.io/mdgart/systemi:latest
docker compose pull
docker compose up -d

Reverse Proxy (Production)

For production deployments, place a reverse proxy (nginx, Caddy, Traefik) in front of Systemi:

nginx.conf
server {
    listen 443 ssl;
    server_name systemi.yourdomain.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://localhost:3000;
        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;
    }
}

Backup & Restore

Back up your PostgreSQL database regularly:

Backup
docker compose exec db pg_dump -U systemi systemi > backup-$(date +%Y%m%d).sql

Restore from backup:

Restore
docker compose exec -T db psql -U systemi systemi < backup-20260309.sql

Firewall Rules

For maximum security, configure your firewall to allow only these connections:

DirectionPortDestinationPurpose
Inbound443Your serverHTTPS access
Outbound443*.atlassian.netJira API
Outbound443api.github.comGitHub API
Outbound443slack.comSlack API
Outbound443api.openai.comOpenAI (optional AI features)
Outbound443generativelanguage.googleapis.comGoogle Gemini (optional AI features)
Internal5432PostgreSQLDatabase

Managed Single-Tenant Deployment

For customers who prefer not to manage infrastructure, Systemi offers managed single-tenant deployments:

  • Dedicated instance: Your own Systemi instance running in an isolated cloud environment
  • Dedicated database: Your data is stored in a dedicated PostgreSQL instance (not shared)
  • Custom domain: Access via your own subdomain (e.g., systemi.yourdomain.com)
  • Managed updates: We handle upgrades, backups, and monitoring
  • SLA-backed: Custom SLAs available for enterprise customers

Contact sales for pricing and setup.

Troubleshooting

Can't pull the image

  1. The image is private — you need registry credentials (provided with your contract)
  2. Run echo $GHCR_REGISTRY_TOKEN | docker login ghcr.io -u $GHCR_REGISTRY_USER --password-stdin before pulling
  3. Verify GHCR_REGISTRY_USER and GHCR_REGISTRY_TOKEN are set correctly
  4. Contact support if credentials are missing or expired

Application won't start

  1. Check logs: docker compose logs app
  2. Verify DATABASE_URL is correct in .env
  3. Ensure the database is accessible: docker compose exec app node node_modules/prisma/build/index.js db push

License key errors

  1. Verify SYSTEMI_LICENSE_KEY is set in .env
  2. Check expiration date
  3. Contact support for renewal

Email not working

  1. By default, Systemi uses its own SMTP relay — no configuration needed. Check logs: docker compose logs app | grep Email
  2. If using custom SMTP, verify SMTP_HOST, EMAIL, and PASSWORD in .env
  3. Some SMTP providers require app-specific passwords or allowed sender verification

Need help?

Our team is available to help with deployment, configuration, and ongoing support.

Contact Support