Skip to content

Stargate Docker deployment

Prerequisites

Server Requirements:

Please refer to Recommended Requirements

  • Docker will be installed automatically if missing
  • Ensure there is an internet connection on the machine where you are installing Stargate services
  • Ensure traffic is properly configured to reach the Stargate instance

Step 1: Configure Customer Settings

Tip

You can clone our repository with all data and sample configs inside with a command:

git clone https://github.com/Health-Info-Net-AG/Stargate-deployment.git

If you do not have git installed, you can always get an archive with all files inside. Download it via the following link. Download as ZIP

The installer creates customer-config.sh automatically from the bundled template on first run, so a fresh install needs no manual configuration. If you prefer to create it yourself, copy the template:

cp customer-config-prod.example.sh customer-config.sh

You do not need to edit it - every value is either auto-detected or configured later via the dashboard:

Setting How it is set
SERVER_STATIC_IP Auto-detected from the server's primary network interface.
CUSTOMER_NAME Defaults to the system hostname.
DEPLOYMENT_NAME Derived from CUSTOMER_NAME (used in log labels and the Alloy hostname).
Passwords & keys (POSTGRES_PASSWORD, S3_SECRET_KEY, VAULT_TOKEN, WG_PRIVATE_KEY) Generated securely on first run and saved back to customer-config.sh.

Mail domains, the mail hostname, S/MIME certificates, and WireGuard peers are all configured at runtime via the dashboard after the stack is up - they are not part of customer-config.sh.

Behind NAT or a floating IP?

Auto-detection uses the IP of the server's primary interface. If your server is reached on a different public or floating IP (common with NAT), set SERVER_STATIC_IP to that public IP in customer-config.sh before installing, so the dashboard and Keycloak login URLs point at the reachable address. Otherwise leave it empty.

Auto-derived settings — leave empty unless you need to override:

Setting Derived from Default
MXENGINE_PUBLIC_ADDRESS SERVER_STATIC_IP http://<SERVER_STATIC_IP>:8084

S/MIME certificate settings:

Setting Description Default
CERT_CA_IRISAGENT_DOMAIN CA domain for certificate issuance via WireGuard tunnel hintest.ch

Note

WireGuard peer setup is performed at runtime via the dashboard (/installation page). Peer details are configured per deployment after the stack is up - they are not part of customer-config.sh.

WireGuard local settings (typically left at defaults):

Setting Default Description
WG_PRIVATE_KEY (auto-generated) Generated by IRISAgent on first run, then saved to customer-config.sh
WG_LOCAL_IP SERVER_STATIC_IP Auto-derived. Only override if you need a different tunnel address.
WG_INTERFACE_PORT 19818 WireGuard tunnel port (both TCP and UDP are exposed)
WG_TRANSPORT_MODE tcp Transport protocol: tcp (default, works through most firewalls) or udp

Optional settings (have sensible defaults):

Setting Default Description
POSTGRES_PASSWORD (auto-generated) Auto-generated 24-character random password if empty
S3_SECRET_KEY (auto-generated) S3 secret key for object storage
OUTBOUND_SEALER_MX_DOMAIN hintest.ch Sealer MX domain for outbound seal delivery
POLICY_SYNC_REPO_URL GitHub HIN Stargate policies Git repo URL for OPA/Rego policy sync
LOKI_URL (unset) Loki endpoint for centralized log shipping (e.g. https://loki.example.com)

Auto-generated (do not set manually):

  • VAULT_TOKEN — Generated by Vault during first initialization, saved to customer-config.sh
  • WG_PRIVATE_KEY — Generated by IRISAgent on first run, saved to customer-config.sh

Step 2: Deploy to a Server

Tip

You can clone our repository with all data and sample configs inside with a command:

git clone https://github.com/Health-Info-Net-AG/Stargate-deployment.git && \
  cd Stargate-deployment-main

If you do not have git installed, you can always get an archive with all files inside and extract it:

wget https://github.com/Health-Info-Net-AG/Stargate-deployment/archive/refs/heads/main.zip && \
  unzip main.zip && \
  rm main.zip && \
  cd Stargate-deployment-main

Manually copy files to the server

scp -r docker-compose/* your-server:/path/to/stargate/

SSH to server

ssh your-server
cd /path/to/stargate

Create customer config from template and fill in required settings (see Step 1)

cp customer-config-prod.example.sh customer-config.sh
nano customer-config.sh   # Fill in required settings (see Step 1)

Run installation

chmod +x scripts/*.sh
./scripts/install.sh

Step 3: What Install Does

The install script (install.sh) performs the following steps:

  1. Check dependencies — Detects Docker, Docker Compose, and jq. If missing, installs them automatically (supports Ubuntu/Debian, RHEL/AlmaLinux/Rocky).
  2. Load and validate customer-config.sh — Checks required fields (SERVER_STATIC_IP, CUSTOMER_NAME, DEPLOYMENT_NAME). Auto-derives optional fields (MXEngine URL, etc.).
  3. Generate .env from customer config — Auto-generates passwords if not set.
  4. Start all services via Docker Compose (infrastructure + applications).
  5. Initialize Vault — The vault-init container initializes, unseals, and creates KV-v2 secret mounts. Optionally writes the WireGuard private key to Vault.
  6. Save Vault keys to secrets/vault-keys.json and update .env with the root token. The token is also saved to customer-config.sh for persistence across VM recreations.
  7. Restart application services to pick up the Vault token.
  8. Save WireGuard private key to customer-config.sh — extracted from Vault after IRISAgent generates it.
  9. Set up daily backup cron job (runs at 2:00 AM).

After install completes, the stack is running but no mail domains, S/MIME certificate, or WireGuard peer are set up yet. Continue with Step 4: Onboard via the dashboard.

Step 4: Onboard via the dashboard

After installation, complete onboarding through the dashboard at https://<SERVER_STATIC_IP>. The dashboard walks you through three pages in order:

/installation — WireGuard peer setup

Performs the nonce/HIN handshake to establish a WireGuard peer connection, and saves the resulting WireGuard configuration to the IRISAgent service.

/onboarding — S/MIME certificate

Generates the S/MIME signing key and CSR via the smimekeys service and submits the CSR to the CA over the now-established WireGuard tunnel. (This replaces the previous script-based certificate flow.)

/mail — Mail domains and relay configuration

Submits hostname and the list of relay domains to the mtaconf service over its REST API. The daemon applies the configuration to Stalwart without restarting the container.

Adding or changing domains later

Re-open the /mail page in the dashboard, edit the domain list, and submit. The daemon applies the change at runtime - no script invocation, no .env edit, no service restart needed.

Step 5: WireGuard Peer Registration

The S/MIME CSR submission on /onboarding will fail if your Stargate instance is not yet registered as a WireGuard peer on the HIN CA side. This is the most common issue during initial setup.

The dashboard's /installation page handles the WireGuard peer registration automatically via the nonce/HIN handshake. If the automatic registration fails, manual registration can be done by providing the following values to HIN:

  1. WireGuard public key — extract from irisagent logs:
docker compose logs irisagent | grep "public key"
  1. DEPLOYMENT_NAME — from your customer-config.sh
  2. SERVER_STATIC_IP — the public IP of your Stargate server
  3. WG_INTERFACE_PORT — only if you changed it from the default 19818

After peer registration is confirmed:

Re-run the /onboarding page in the dashboard to regenerate the CSR and submit it through the now-up tunnel.

To verify the tunnel before requesting the certificate:

Restart just irisagent

docker compose restart irisagent

Check for successful WireGuard handshake

docker compose logs irisagent 2>&1 | grep -i "handshake\|peer"

Tip

Check your firewall: Port 19818/TCP must be open both directions inbound and outbound on the Stargate server.

Step 6: Post-Onboarding Recommendations

Once the certificate is issued and mail is flowing, two configuration items are strongly recommended for any production deployment. Skipping them does not break encryption, but it will degrade your sender reputation, cause "we can't verify the sender" warnings in Outlook/Gmail, and can eventually lead to outbound mail being blocklisted.

Step 6.1 SPF / DKIM / DMARC for sender domains

The Stargate sends mail from its own public IP on behalf of your users. Without proper DNS authentication records, recipients will see "we can't verify this sender" warnings and may reject the mail.

For complete instructions on configuring SPF, DKIM, DMARC, and PTR records, see the DNS Setup Guide.

At minimum, for each domain you route through the Stargate:

  • SPF: add ip4:<STARGATE_IP> to the domain's TXT record
  • DMARC: publish v=DMARC1; p=none at _dmarc.<YOUR_DOMAIN>
  • PTR: set reverse DNS for the Stargate IP to match MAIL_HOSTNAME

By default, after the Stargate signs/encrypts an outbound mail it delivers directly to the recipient's MX. This works, but the connecting IP is your Stargate's IP - and unless that IP has years of warm reputation, it can end up on third-party blocklists (e.g. Barracuda, Abusix), causing intermittent delivery failures.

The recommended pattern is to send the signed mail back through your M365 / Exchange tenant so that the final hop to the internet is Microsoft's well-reputed infrastructure. The Stargate still signs and policy-checks every message; only the last hop changes. This mirrors the original HIN MGW "Send to MX" connector pattern.

Stargate side — per-domain relay

Configure per-domain relay through the dashboard's /mail page. Each domain can be mapped to its own M365 / Exchange inbound endpoint; the dashboard sends the mapping to mtaconf's REST API and Stalwart is reconfigured at runtime.

After mxengine signs the mail, Stalwart will hand it back to your tenant on port 25 with TLS instead of delivering directly to the recipient's MX. See Exchange-integration.md for the full per-domain syntax.

M365 / Exchange Online side

You essentially recreate the same connector + transport rule set as the old HIN MGW (the original HIN MGW O365 manual is the reference - the same five rules apply). The minimum is:

  1. Inbound connector - accept mail from the Stargate, identified by TLS certificate (the cert subject must match a domain accepted in your tenant). A self-signed cert on the Stargate will be rejected by this connector - use a valid CA-issued cert (Let's Encrypt is fine).
  2. Outbound connector "Send to MX" - delivers to the recipient's MX, activated only by transport rule.
  3. Transport rule set_header - tags outbound mail with a header like outgoing: outgoing_<domain> before it leaves O365 the first time, so the return trip can recognize it.
  4. Transport rule outgoing_to_mx - matches the outgoing_<domain> header on mail coming back from the Stargate and routes it via the "Send to MX" connector.
  5. Transport rule mgw_bypass_antispam - bypasses spam filtering on mail coming back from the Stargate.

mxengine does not strip arbitrary headers, so the outgoing_<domain> tag set by set_header survives the round-trip and triggers outgoing_to_mx correctly.

Why this pattern matters

With the relay-back configuration, the public sender to the internet is Microsoft. Combined with correct SPF/DKIM/DMARC (section 6.1), recipients see a Microsoft IP with spf=pass and dkim=pass aligned to your domain - which is the cleanest reputation profile you can give them.

See Exchange-integration.md for full step-by-step instructions including screenshots.

Subsequent Starts (after reboot)

The installer enables a stargate systemd unit, so the stack starts automatically on boot. To start it manually:

sudo systemctl start stargate

This runs start.sh, which:

  1. Starts infrastructure services
  2. Unseals Vault using stored keys
  3. Starts application services

(./scripts/start.sh still works directly if you prefer.)

Stop Services

sudo systemctl stop stargate

(or ./scripts/stop.sh directly)

This stops containers but preserves all data.

Data Persistence

All data is stored in Docker volumes and persists across restarts.

Service Volume Data
PostgreSQL postgres_data All databases (smimekeys, policy, irisagent, mxengine)
Vault vault_data Encryption keys, secrets, S/MIME keys
SeaweedFS seaweedfs_data Object storage (messages, attachments)
Stalwart stalwart_data Mail server state

Safe Operations (data preserved)

Stop and start:

sudo systemctl stop stargate
sudo systemctl start stargate

Or using the scripts directly:

./scripts/stop.sh
./scripts/start.sh

Do not use docker compose commands directly

Always use systemctl or the provided scripts (start.sh / stop.sh) to manage the deployment. Running docker compose up, docker compose down, or docker compose restart directly will not unseal Vault, leaving dependent services unable to start. The start.sh script handles the Vault unseal procedure automatically.

Vault Sealing Behavior

Vault becomes sealed when its container restarts. This is a security feature.

The start.sh script (and the systemd service) automatically unseal Vault using the keys stored in secrets/vault-keys.json. This is why you must always use the provided scripts or systemd service to manage the stack.

⚠ Destructive Operations (data deleted)

Warning

These commands DELETE ALL DATA - use with caution!

You can only restore data if you perform backup operations before and save the backup in a safe place.

Danger

Delete everything (volumes, secrets, config)

./scripts/purge.sh

Or manually remove volumes. The -v flag removes volumes

docker compose down -v

Scripts Reference

Script Purpose
install.sh First-time installation (Docker, Vault). Domain/cert/peer setup happens in the dashboard afterwards.
update.sh Update service images (preserves Vault token, recreates containers)
start.sh Start services and unseal Vault
stop.sh Stop containers (data preserved)
backup.sh Full backup (database, Vault keys, config, certificates)
restore.sh Restore from backup archive (works on fresh machine)
purge.sh ⚠ Delete ALL data (requires confirmation)
health-check.sh Comprehensive health check of all services (exit 0 = healthy, 1 = failures)
init-vault.sh Vault initialization (used by vault-init container, not called directly)
init-keycloak.sh Keycloak admin password setup (used by keycloak-init container, not called directly)
gather-app-versions.sh Collects app versions from /liveness endpoints for node-exporter (runs in version-collector container)

Configuration Files

File Purpose
customer-config-prod.example.sh Template for customer settings (copy to customer-config.sh)
customer-config.sh Customer-specific settings (created from template, fill in before install)
.env Generated environment file (created by install.sh)
secrets/vault-keys.json Vault unseal keys and root token (back up securely!)
secrets/signing-key.csr Generated CSR for S/MIME certificate

Support

Support

For any questions or issues related to the deployment and operation of the Stargate appliance, please contact HIN support.

Please include relevant information such as the customer name, appliance version, and screenshots/logs where applicable, to help us process your request efficiently.