Table of Contents
Stargate Deployment Information¶
🇩🇪 Hinweis zu Übersetzungen | 🇫🇷 Remarque concernant les traductions
🇮🇹 Avvertenza sulle traduzioni | 🇬🇧 Translation notice
Für verlässliche Informationen nutzen Sie bitte die offiziellen Übersetzungen über den Sprachschalter . Automatische Browser-Übersetzungen können Inhalte verfälschen.
Pour obtenir des informations fiables, veuillez utiliser les traductions officielles accessibles via le sélecteur de langue . Les traductions automatiques du navigateur peuvent déformer le contenu.
Per informazioni affidabili, utilizzare le traduzioni ufficiali accessibili tramite il selettore della lingua . Le traduzioni automatiche del browser possono alterare i contenuti.
For reliable information, please use the official translations available via the language selector . Automatic browser translations may distort the content.
Pre-Requisites¶
Please ensure that all necessary preparatory steps have been completed before the HIN Gateway migration or new installation activities begin.
The following items must be available or confirmed before the installation:
-
Credentials will be delivered to you by HIN
- VM credential
- Keycloak credential
- Activation code
-
Export of private key(s)
Info
Export of private keys is only for customers moving from existing MGW to a new HIN Gateway
- If you are working on a Windows machine that has access to the Mail Gateway VM via port 22, we can support you during the call in enabling the private key export from the MGW.
- If you do not have access to such a machine, please contact HIN Support by email or phone (support@hin.ch / 0848 830 740) to help you establish a support connection via System Administration → Support Connection → Connect.
For customers with multiple domains
Note: applicable for all multi-domain migration scenarios!
To reduce the time required to execute the migration, we encourage customers to complete the following steps before the planned migration date and session:
- Export the private key for each domain.
- Identify and document the incoming and outgoing mail flow for each domain.
Please contact HIN Support to obtain the unlock code required to export the private keys.
- Download latest version of VM image
- Firewall requirements for WireGuard.
Configure the WireGuard port 19818 (TCP/UDP) in your firewall:
- Incoming and outgoing traffic
- Allow traffic: any-to-HIN Gateway and HIN Gateway-to-any
-
DHCP access should be available. For more information see "Installation Guidelines".
-
Backup requirements - see "Annex 1 - Backing up and restoring the appliance settings".
Info
Backup requirements is only for customers moving from existing MGW to a new HIN Gateway
- Confirmation that the existing MGW will not be deleted until acceptance has been completed.
Info
Keeping the existing MGW available till acceptance report have been completed is only for customers moving from existing MGW to a new HIN Gateway
- Access to DNS, mail server connectors, transport rules, and relay settings.
Deployment Information¶
Installation Options¶
- VM image installation:
Second disk required: the Data Disk
The appliance uses two disks: the OS disk from the image, and a separate Data Disk that holds all configuration, secrets, mail, and databases. Keeping them apart lets an image upgrade replace the OS without touching your data.
The VMware OVA already includes this disk. On every other platform (Proxmox, Hyper-V, Azure, Cloudscale) the image is a single OS disk, so you must attach a second, blank disk of at least 30 GB before the first boot.
Do not format or partition it yourself. On first boot the appliance formats the blank disk (label VEREIGN-DATA) and mounts it at /var/data. Without it, the first boot fails its health check and rolls back.
🖨️
You can get this documentation printed or saved as PDF, please visit our Print page view.
Exchange Integration¶
- Exchange integration - Configure Microsoft Exchange (Online and On-Premises) connectors and transport rules to route mail through Stargate
Server Requirements¶
| Minimum | Recommended | |
|---|---|---|
| CPU, Cores | 4 | 6 |
| RAM, GB | 8 | 12 |
| SSD, GB | 60 | 60 |
Common Requirements¶
- Root access: Must be run as root or with
sudo - Supported distributions:
- RHEL 8, 9 and 10 compatible distributions such as Alma Linux, Rocky Linux, CentOS Stream
- Ubuntu 22 and 24
- Debian 11, 12 and 13
- Real IPv4 address
- Valid DNS records. Your domain must have:
- MX records pointing to your mail servers
- SPF record defining allowed sending networks
- Server must be able to resolve DNS (MX, SPF, A records)
- Used for mail routing and SPF-based network allowlisting
Inbound Network Access (firewall must allow)¶
| Port | Protocol | Purpose |
|---|---|---|
25 |
TCP | SMTP - receiving mail from external servers or from an MTA installed in front of the HIN Gateway. |
19818 |
UDP+TCP | WireGuard - encrypted tunnel for agent-to-agent communication. Read our Security Assessment WireGuard |
Inbound VM Access (your administrative machine to the HIN Gateway VM)¶
Info
These firewall rules should be applied only between your administrative machine and the HIN Gateway VM. There is no need to expose these ports to the Internet.
| Port | Protocol | Purpose |
|---|---|---|
80 |
TCP | Redirects HTTP traffic to HTTPS |
443 |
TCP | Used to manage the HIN Gateway through the web dashboard |
8180 |
TCP | Used by Keycloak to authenticate users for the HIN Gateway dashboard |
8190 |
TCP | Optional. Required for troubleshooting and viewing logs |
22 |
TCP | Optional. Required for troubleshooting and modifying configuration |
Outbound Network Access (server must reach)¶
| Destination | Port | Protocol | Purpose |
|---|---|---|---|
registry-1.docker.io, auth.docker.io, production.cloudflare.docker.com |
443 |
TCP | Docker Hub image registry |
quay.io |
443 |
TCP | Container registry (Keycloak, oauth2-proxy) |
github.com |
443 |
TCP | Policy repository (policy-sync) |
| Your own Loki endpoint (e.g. loki.example.com) | 443 |
TCP | Optional. Only needed if you provide your own Loki instance the stack should ship logs to (Alloy → Loki) |
| Update Server of alpine, almalinux, etc. | 80 |
TCP | Various Update servers - https://hub.docker.com/ |
| Destination mail servers | 25 |
TCP | Outbound mail delivery via MX lookup, or delivery to the next MTA configured as the outbound server. |
| Standard DNS queries and responses | 53 |
UDP + TCP | DNS resolve |
ntp.metas.ch (default NTP server) |
123 |
UDP | NTP synchronizes the clocks of computers, servers, network devices, and virtual machines with accurate time sources |
| WireGuard peers (HIN network) | 19818 |
UDP+TCP | WireGuard - encrypted tunnel for agent-to-agent communication |
witness-{1,2,3}.verify-mail.hin-infra.ch |
443 |
TCP | HIN KERI witness pool - required for agent identity verification (idagent / watcher) |
app.hin.ch |
443 |
TCP | HIN member / mail-domain list (mxengine) |
apisix.verify-mail.hin-infra.ch |
443 |
TCP | HIN gateway registration during onboarding (dashboard) |
Using your own NTP server
The appliance is preconfigured to use ntp.metas.ch (Swiss Federal Institute of Metrology).
If your network does not permit outbound NTP, configure your own time server instead of
opening port 123 to the internet - an internal server is fully supported.
To set your own, create the file below on the appliance and reload. List as many servers as you need, one per line:
sudo tee /var/data/vereign/chrony/ntp.sources <<'EOF'
pool ntp1.example.local iburst
pool ntp2.example.local iburst
EOF
sudo chronyc reload sources
chronyc sources -v
The file must be named exactly ntp.sources. With that name your list replaces the
default; under any other name it is added to the default instead. Delete the file and
reload to go back to ntp.metas.ch.
Accurate time is not optional. If the clock drifts, certificate validation, message signing and sign-in sessions all begin to fail in ways that are hard to diagnose.
Important note regarding operation on Microsoft Azure
**Operation on Microsoft Azure**
For the SMTP relay connection between the HIN Gateway and Exchange Online, outgoing data traffic via TCP port 25 must be permitted.
Microsoft Azure blocks outbound connections via port 25 for most subscription models. HIN has no control over the availability or activation of this port by Microsoft. Operating the HIN Gateway on Azure is therefore **not a standard supported deployment scenario**. Exceptions apply in particular to [certain Microsoft Enterprise subscriptions](https://learn.microsoft.com/en-us/troubleshoot/azure/virtual-network/troubleshoot-outbound-smtp-connectivity). Before installing on Azure, please check whether your subscription permits outbound SMTP traffic via TCP port 25.
If you have any questions regarding the appropriate deployment option, please contact your HIN contact person at an early stage.
Firewall note
Depending on your firewall or NAT configuration, you may need to explicitly allow traffic for the required ports. Refer to your firewall and NAT documentation for details.
The VM must be able to accept incoming connections on the required service ports and send responses back to the requester. With a stateful firewall (such as iptables using conntrack), return traffic is automatically allowed by the ESTABLISHED,RELATED rules.
Example iptables configuration:
# Allow return traffic for established connections
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow inbound TCP connections to the open ports
iptables -A INPUT -p tcp -m multiport --dports 25,19818 -j ACCEPT
# Allow outbound TCP connections to the open ports
iptables -A OUTPUT -p tcp -m multiport --dports 25,19818 -j ACCEPT
# Allow inbound UDP port 19818 for Wireguard
iptables -A INPUT -p udp --dport 19818 -j ACCEPT
# Allow outbound UDP port 19818 for Wireguard
iptables -A OUTPUT -p udp --dport 19818 -j ACCEPT
# Additional Services that VM shall be able to reach
# DNS
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT
# NTP
iptables -A OUTPUT -p udp --dport 123 -j ACCEPT
# HTTP
iptables -A OUTPUT -p tcp --dport 80 -j ACCEPT
# HTTPS
iptables -A OUTPUT -p tcp --dport 443 -j ACCEPT
Recommended Migration Approach by Number of Domains¶
Customers with 3 Domains or Fewer
- HIN recommends migrating all domains at once.
- HIN will support the customer in successfully migrating the first domain.
- Once the first domain has been migrated successfully, the customer can migrate the remaining domains independently.
- This approach keeps the migration simple and avoids the need for a parallel environment.
Customers with More Than 3 Domains
- HIN recommends setting up a parallel environment alongside the existing MGW.
- Domains can then be migrated gradually to the new environment.
- HIN will support the customer in successfully migrating the first domain.
- After the first successful migration, the customer can decide how and when to move the remaining domains to the new environment.
Contact us¶
Support
For any questions or issues related to the deployment and operation of the HIN Mail (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.
Installation Guides
HIN Gateway – Technical Guide for New Installation and Migration¶
Introduction¶
This document provides a comprehensive guide to the technical installation of, and migration to, the new HIN Gateway (“HIN Gateway Appliance”).
The guide is intended for HIN customers, IT administrators, and system engineers who are responsible for deploying and configuring the new HIN Gateway and, where applicable, migrating from the existing Mail Gateway (MGW) to the new solution.
The HIN Gateway is a secure email gateway solution that enables trusted, encrypted, and policy-driven communication within the HIN Trust Circle. It acts as a central intermediary between internal email infrastructures and external communication partners, ensuring that email traffic is transmitted securely, complies with the organizations policies, and meets HIN’s security standards.
Overview of the mail flow¶
- Incoming emails are routed via the HIN Gateway, where they are validated, decrypted (if necessary) and checked against trust and security policies before being forwarded to the internal mail server.
- Outgoing emails are sent from internal systems to the HIN Gateway, where encryption, routing and policy enforcement are applied before they are transmitted to external recipients.
- Communication between HIN gateways is secured by peer certificates and WireGuard tunnels, ensuring trusted communication between domains.
Installation and migration process¶
The structured, step-by-step procedure described in this document covers both new HIN Gateway installations and migrations from an existing HIN Mail Gateway (MGW). Depending on the deployment scenario, individual steps may apply only to migrations.
- Preparation and deployment planning, including fallback planning where applicable
- Installation and configuration of the HIN Gateway
- Domain activation and certificate validation
- Integration into the existing mail environment and routing configuration
- Testing, transition to production and post-deployment validation
- For migrations: retirement of the existing MGW after successful validation
Migration
HIN's objective is to ensure a secure, smooth and fully validated deployment with minimal disruption to operations and uninterrupted continuity of email services. In migration scenarios, the existing MGW should remain available as a fallback option until the HIN Gateway has been successfully validated in production. It should only be decommissioned once the migration has been completed and stable operation has been confirmed.
Frequently asked questions¶
Can I perform the installation or migration on my own?
Yes, the installation or migration can be completed entirely by the customer.
For migration scenario the only exceptions is "Step 1.3 - Export private key(s)". For security reasons and to keep your private key safe, you must contact HIN Support or join the planned migration call to receive the code required to export the private key from the currently operating Mail Gateway.
If the installation or migration cannot be completed successfully, please join the planned support call with our engineers.
Will there be any outage in email delivery during the setup process?
Migration: Between "Step 1.5 - Shutdown existing MGW VM" and "Step 18 - Configure mail server", all emails will be queued on the mail server. Once "Step 18 - Configure mail server" has been completed, the queued emails will be sent out or delivered to the mailbox.
New installation: While you are configuring the email flow rules, all emails will be queued on the mail server. Once "Step 18 - Configure mail server" has been completed, the queued emails will be sent out or delivered to the mailbox.
Will any emails be lost during the installation and migration?
No, no emails will be lost during the installation and migration. Some emails might be delayed.
Overview of the installation steps¶
| Step | Topic | Responsibility | Migration | New installation |
|---|---|---|---|---|
| 0 | Check prerequisites | Customer | Yes | Yes |
| 1.1 | Smoke test | Customer | Yes | N/A |
| 1.2 | Backing up the existing MGW | Customer | Yes | N/A |
| 1.3 | Export private key(s) | Customer / HIN | Yes | N/A |
| 1.4 | Contingency plan / fallback scenario | Customer | Yes | N/A |
| 1.5 | Shutdown existing MGW VM | Customer | Yes | N/A |
| 2 | WireGuard | Customer | Yes | Yes |
| 3 | Select target VM | Customer | Yes | Yes |
| 4 | Load VM image | Customer | Yes | Yes |
| 5 | Network connection to the VM | Customer | Yes | Yes |
| 6 | Access via the browser | Customer | Yes | Yes |
| 7 | Enter activation code | Customer | Yes | Yes |
| 8 | Mesh network setup | Customer | Yes | Yes |
| 9 | Establishing secure mesh network | Customer | Yes | Yes |
| 10 | Login to Keycloak | Customer | Yes | Yes |
| 11 | Update password | Customer | Yes | Yes |
| 12 | Update account information | Customer | Yes | Yes |
| 13 | Initial configuration and domain setup | Customer | Yes | Yes |
| 14 | Configure mail transport | Customer | Yes | Yes |
| 15 | Configure whitelist headers | Customer | Yes | Yes |
| 16 | Peer certificates | HIN | Yes | Yes |
| 17 | Validate peer certificates | Customer | Yes | Yes |
| 18 | Configure mail server | Customer | Yes | Yes |
| 19 | Test and validate | Customer | Yes | Yes |
| 20 | Change the password of the VM | Customer | Yes | Yes |
| 21 | Take existing MGW out of service | Customer | Yes | N/A |
| Annex 1 | Backing up and restoring the appliance settings | Customer | Yes | N/A |
Detailed steps¶
Step 0 - Check prerequisites¶
Please ensure that all necessary preparatory steps have been completed before the HIN Gateway migration activities begin.
The following items must be available or confirmed before the installation:
-
Credentials will be delivered to you by HIN
- VM credential
- Keycloak credential
- Activation code
-
Export of private key Note: Applicable only for migration case
- If you are working on a Windows machine that has access to the Mail Gateway VM via port 22, we can support you during the call in enabling the private key export from the MGW.
- If you do not have access to such a machine, please contact HIN Support by email or phone (support@hin.ch / 0848 830 740) to help you establish a support connection via System Administration → Support Connection → Connect.
- Download latest version of VM image
- Firewall:
- Allow traffic: any-to-HIN Gateway and HIN Gateway-to-any
- WireGuard, please refer to Server Requirements - Inbound Network Access:
- Configure the WireGuard port
19818(TCP/UDP) in your firewall- Incoming and outgoing traffic
- Configure the WireGuard port
- WireGuard, please refer to Server Requirements - Inbound Network Access:
- Allow traffic: your administrative machine-to-HIN Gateway VM
- Installation requirements:
- HTTPS port
443- Incoming and outgoing traffic
- Keycloak port
8180- Incoming and outgoing traffic
- HTTPS port
- Troubleshooting requirements (optional needed to see logs, modify all parameters):
- SSH port
22- Incoming and outgoing traffic
- Dozzle port
8190- Incoming and outgoing traffic
- SSH port
- Installation requirements:
- Allow traffic: any-to-HIN Gateway and HIN Gateway-to-any
- DHCP access should be available for "Step 5 - Network connection to the VM" (recommended).
- Backup requirements - see "Annex 1 - Backing up and restoring the appliance settings". Note: Applicable only for migration case
- Note: Applicable only for migration case - Confirmation that the existing MGW will not be deleted until acceptance has been completed.
- Access to DNS, mail server connectors, transport rules, and relay settings.
Why WireGuard?
The WireGuard port fulfils two important functions:
- The HIN Gateway uses this port to obtain peer certificates from the HIN CA.
- It uses this port to establish a secure tunnel to other HIN Gateways, through which secure data exchange (e.g. email traffic) takes place.
!!! tip "Private key export" - Applicable only for migration case If you are working on a Windows machine that has access to the Mail Gateway VM via port 22, we can support you during the call in enabling the private key export from the MGW.
If you do not have access to such a machine, please contact HIN Support by email or phone (**support@hin.ch** / **0848 830 740**) to help you establish a support connection via **System Administration → Support Connection → Connect**.
Step 1.1 - Smoke test¶
Info
This step applies only for single and multi domain migrations
Send test emails to the following recipients, using mailboxes to which you have access so that successful delivery can be verified:
- An HIN email address or an email address within your HIN Community domain, for example:
user@hin.ch - An external email address outside the HIN Community, for example: Bluewin, Gmail, Yahoo, or GMX
For the external recipient, send an email from the HIN Community with (confidential) included in the subject line.
(Confidential) messages outside of HIN cmmmunity
If you are using Outlook and one of the following options to send confidential emails outside the HIN community: * Using a specific email template * Using the Key Lock symbol
Please test the mail flow before migration to confirm that the email is processed and securely delivered.
Please test the mail flow before migration to confirm that the email is processed and securely delivered.
Test mail flow in both directions:
- From the HIN trusted domain to the external email address
- From the external email address to the HIN Community
Verify that all test emails are delivered successfully and that the subject, message content, and attachments (if applicable) are received correctly.
Step 1.2 - Backing up the existing MGW¶
Info
This step applies only for single and multi domain migrations
Create a backup of the existing MGW appliance and ensure that the VM is retained until the migration has been successfully completed and formally accepted. For more information, see "Annex 1 - Backing up and restoring the appliance settings".
Check current MGW routing configuration
Before shutting down the existing MGW, check the following configuration values and note them down. You will likely need them later when configuring the HIN Gateway:
- Log in to the MGW and go to "Mail System → Outgoing server" and check whether anything is configured there.
- For each domain hosted on the MGW, go to
Mail System → <domain> → Forwarding serverandMail System → <domain> → Send ALL outgoing mails from this domain to the following SMTP server, and record the current values.

MGW header check
If you use the Header check option in the MGW, note down the configured value as well. You can set up the same header check later in the HIN Gateway.
Step 1.3 - Export private key(s)¶
Info
This step applies only for single and multi domain migrations
For multi-domain migration perform the operation for each domain
HIN assistance required
An unlock code is required for this step. The code is provided by a HIN Support Engineer.
If you would like to continue the installation on your own, please contact HIN Support to request the unlock code. Otherwise, the unlock code will be provided during the planned migration call.
- Log into the existing MGW webGUI.
- Open "Mail System".

- Run the application by clicking on
HIN_Migration-Tool_v*.exeif you want to install it yourself. Alternatively, you can wait until the migration call, where the support engineer will assist you with the installation.

- Enter the unlock code that the support engineer provides to you.

- Select "Enable export".

- Enter the MGW IP address.

- Wait for confirmation.

- Select the trusted domain in the MGW webGUI.

- Scroll down and select the managed fingerprint.

- Scroll down to the "PKCS12 download" category (you may optionally enter a password to encrypt the key). Press "Download PKCS12" and save the
*.p12file on the computer.

- Return to the
HIN_Migration-Tool_v*.exeapplication and disable the Export button.

Step 1.4 - Contingency plan / fallback scenario¶
Info
This step applies only for single and multi domain migrations
Rollback scenario - if a rollback is required:
- Stop the new HIN Gateway.
- Power on the existing MGW.
- Verify that inbound and outbound email traffic is functioning correctly via the existing MGW.
- For multi-domain migration perform the verification operation for each domain
Step 1.5 - Shutdown existing MGW VM¶
Info
This step applies only for single and multi domain migrations
Shut down the existing MGW VM.
Warning
This step will interrupt the mail flow. During the interruption, emails will be queued on the mail server and delivered after the installation has been completed (see "Step 18 - Configure mail server").
Step 2 - WireGuard¶
Ensure you have configured the WireGuard port 19818 (TCP/UDP) in your firewall:
- Incoming and outgoing traffic
- Allow traffic: any-to-HIN Gateway and HIN Gateway-to-any
Step 3 - Select target VM¶
Select one of the available virtual images and provision it as described in the installation guide on the HIN Gateway service page:
Info
For security and supportability reasons, ensure that your hypervisor is not running an end-of-life version. The HIN Gateway appliance is supported on the latest hypervisor release and the immediately preceding major version.
- VM Image Installation:
- Configuration of Microsoft Exchange
Step 4 - Load VM image¶
Upload the selected VM image to your hypervisor.
Second disk required — the Data Disk
The appliance uses two disks: the OS disk from the image, and a separate Data Disk that holds all configuration, secrets, mail, and databases. Keeping them apart lets an image upgrade replace the OS without touching your data.
The VMware OVA already includes this disk. On every other platform (Proxmox, Hyper-V, Azure, Cloudscale) the image is a single OS disk, so attach a second, blank disk of at least 30 GB before the first boot.
Do not format or partition it yourself. On first boot the appliance formats the blank disk (label VEREIGN-DATA) and mounts it at /var/data. Without it, the first boot fails its health check and rolls back.
Step 5 - Network connection to the VM¶
Ensure that the VM has a network connection and that a static IP address has been assigned to it.
Option A: Configure the IP address of the virtual machine directly in the hypervisor you are using.
Option B: You can configure your router's DHCP server to always assign the same IP address based on the VM's MAC address.
Option C: Log in locally via the VM console and manually configure a static IP address. NOTE: The VM image runs an automatic installation during the first boot. If the network is not configured at this stage, the installation will fail because the server’s IP address cannot be determined.
Add an IP address on Linux:
-
Run the "nmtui" command in the console
-
Use the arrow keys to navigate, then press "Enter" to select the "Ethernet connection" for which you want to change the IP address.

- Navigate to "IPv4 Configuration" and change the setting from "Automatic" to "Manual".

- Use the arrow keys to navigate to the fields where you can enter the IP address, gateway, and DNS server. Then select "OK".

-
After saving the IP address configuration, run the following command in the console:
Tip
If you used Option C and configured the network manually, you must run the following commands:
The installation script automatically detects the server's IP address from the default route on every run - no manual edit of customer-config.sh is needed. Any reachable IP address, whether public or private, is sufficient. The actual public endpoint is configured later through the dashboard.
Behind NAT or a floating IP?
If your server is reached on a different public or floating IP than its own network interface (common with NAT), set SERVER_STATIC_IP to that reachable IP in customer-config.sh before running install.sh. Otherwise leave it empty so it auto-detects.
After the scripts have completed successfully, proceed to "Step 6 – Access via the browser"
Question
If you do not have the HIN admin credentials, please contact HIN Support by email or phone (support@hin.ch / 0848 830 740). Please refer to Support Section.
Important note regarding operation on Microsoft Azure
Outgoing SMTP communication via TCP port 25 is required for the SMTP relay connection between the HIN Gateway and Exchange Online.
Microsoft Azure blocks outbound connections via port 25 for most subscription models. [For standard Enterprise Agreement (EA) and Microsoft Customer Agreement for Enterprise (MCA-E) subscriptions](https://learn.microsoft.com/en-us/troubleshoot/azure/virtual-network/troubleshoot-outbound-smtp-connectivity), port 25 is generally available, according to Microsoft. Special conditions apply to certain other enterprise configurations.
As these restrictions and any activations are determined exclusively by Microsoft, operating the HIN Gateway on Azure is not a standard supported deployment scenario. HIN has no control over the availability or activation of this port by Microsoft.
Recommended action: Are you planning a migration or an infrastructure change involving Azure? Before installation, you must check whether your Azure subscription permits outbound SMTP communication via TCP port 25. Contact your HIN contact person at an early stage to coordinate the operating option that is suitable for you.
Recommended alternative operating options
If TCP port 25 is not available in your Azure environment, we recommend:
· Operating the HIN Gateway on a local or on-premises VM (as before)
· Operating the HIN Gateway with a hosting provider that complies with data protection regulations
**Outlook: Gateway as a Service**
HIN plans to offer the Gateway as a fully managed service in the future. Would you like to be informed as soon as HIN Gateway as a Service becomes available? Please contact your HIN contact person to register your interest, with no obligation..
Step 6 - Access via the browser¶
Open a browser and enteć the IP address configured for the VM. You should see the initial setup screen.
Reboot the VM
If the machine is not accessible, restart the VM from the console and then try accessing it again through the browser.
Step 7 - Enter activation code¶
Select your preferred language and enter the activation code that you received via email from HIN. Click on "Next".
I do not have an activation code
If you do not have the activation code, please contact HIN Support by email or phone (support@hin.ch / 0848 830 740). Please refer to Support Section.
Step 8 - Mesh network setup¶
Verify the mesh network configuration:
- IP address - The public IP of the outgoing traffic (auto-detected).
- Transport - The transport protocol (default:
tcp). - Port - The WireGuard port (default:
19818).
What is public IP?
This is an IP address that Machine will use to be accessible via internet.
This is not internal Machine IP address after Firewall or NAT, e.g. 10.0.0.0/8, 172.16.0.0/12 or 192.168.0.0/16.
Confirm that the values are correct and click "Next".
Step 9 - Establishing secure mesh network¶
The system will now establish the secure mesh network connection. This step connects the HIN Gateway to the mesh network and synchronies certificates.
Wait until the process completes. The status indicators will show "Up" when the connection is successfully established. Click "Finish".
If the connection fails
If the Iris Agent or certificate synchronization status remains "Down":
- Verify that port
19818(TCP/UDP) is open in your firewall (see "Step 2 - WireGuard"). - Verify that the IP address in "Step 8 - Mesh network setup" is correct and reachable from the internet.
- Restart the process or contact HIN Support by email or phone (support@hin.ch / 0848 830 740).
Step 10 - Login to Keycloak¶
Warning
Port 8180 needs to be open for Keycloak. It does not need to be accessible from the internet to the whole world. Rather, it should be accessible between your administrative machine and the VM you are installing. Otherwise, you will not be able to connect to Keycloak and proceed with the installation.
What to do if I see a connection error
Please check if port 8180 is open from your machine to the VM. As soon as you update the configuration, go back to the UI at https://<VM IP address> and click the "Login" button.
Once the mesh network is established, you will be redirected to the Keycloak login page. Enter the username and password received from HIN.
Question
If you do not have these login details, please contact HIN Support by email or phone (support@hin.ch / 0848 830 740). Please refer to Support Section.
Step 11 - Update password¶
On first login, you will be prompted to change your password. Enter a new secure password and confirm it.
Please make sure you remember the password!
Step 12 - Update account information¶
Complete your account profile by entering your first name and last name. The email address is pre-filled. Click "Submit" to continue.
Step 13 - Initial configuration and domain setup¶
Info
For multi-domain migration perform the operation for each domain domain you are activating at that moment.
On this screen, configure your initial settings:
- Verify that all your current trusted domain(s) within the HIN Community are displayed correctly.
- Select which trusted domain(s) should be Enabled to obtain peer certificates from the HIN Certification Authority (HIN CA).
- Indicate for which domain(s) the
sec.<domain>prefix is already configured ("Use sec-prefix").
How to check if my domain is set up with a Security Prefix?
You can open our online tool in your browser: https://trust.hin.ls-infra.me/, enter sec.<domain>, and click the Check button. If you see the message:
✅ This domain is encrypted.
Then your domain is set up with a Security Prefix, and you have to enable the Use sec-prefix option.
- Verify that the organization name and domain owners are correct.

- Import the existing S/MIME certificate file (
.p12/.pfx) from the existing MGW:- Expand the domain and select the P12/PFX File option.
- If no password has been set for the certificate file, leave the password field empty.
- Click "Import Certificate".
- After the certificate has been imported, the message Certificate imported successfully is displayed.
- Click "Save Configuration" at the end of the page to save the changes.
Warning
- At least one domain must be Enabled to continue with the onboarding process. The "Save configuration" button will only become active once this requirement is met.
- If you notice that not all trusted domains are displayed or that the organization information is incorrect, please contact HIN Support by email or phone (support@hin.ch / 0848 830 740).
Import your existing private key
Note: applicable for migration scenario!
If you do not import the private key from your existing MGW, a new key will be issued. This may result in messages not being decrypted for up to 6 hours, which could lead to data loss.
| Setting | Description |
|---|---|
| Mail server host name | The FQDN of this mail gateway instance (e.g. mail.example.com). |
| Mail server IP addresses | The public IP address(es) of this server. Add additional IPs if the server is reachable on multiple addresses. |
| DNS | DNS of the host which will be used to resolve MX and other DNS records. Please make sure that the entered DNS server is reachable from the HIN Gateway VM. |
Multi-domain gradual migration scenario
During the migration support session, HIN engineers will assist the customer with the migration of one domain. Once the first domain has been successfully migrated, the customer is responsible for migrating any remaining domains independently.
Step 14 - Configure mail transport¶
You will be logged at the HIN gateway dashboard at Domains page
Domains page¶
Info
For multi-domain migration perform the operation for each active domain.
Under the Domains menu, for each available domain you can configure specific transport route:
| Setting | Description |
|---|---|
| Inbound relay | The SMTP relay that receives inbound mail for the selected domain. You can define routing rules based on the sender's domain (external domain) to control where incoming messages are delivered |
| Outbound relay | The SMTP relay for outbound mail for the selected domain. After the gateway processes a message, it forwards it to this server as the next hop, based on the configured local domain. Migration note: this setting corresponds to Forwarding server in the old MGW. |
| Trusted networks | Additional networks allowed to relay through this gateway. For more information please check on "Step 18 - Configure mail server" |
| Configure TLS | TLS certificate settings for SMTP connections and from the Generate TLS certificate button you can generate TLS certificate |
| Email authentication | For all settings under Email authentication section please refer to Email authentication (DKIM ARC SPF DMARC) section |
How to test TLS connection?
You can always test if configured TLS Certificate was applied on your connection to HIN Gateway or not. Execute following command directly from HIN Gateway Terminal:
Or, directly from your local machine:
In output you will see all data related to your TLS connection and used Certificate.
How to convert pfx to pem TLS Certificate?
Use openssl command:
E.g.:
Additional actions:
- Add additional domains by clicking "Add domain", if required.
- if domain is not not HIN secured, it will appear in the
Domainslist as Type: Routed - which mean it can be managed only locally
- if domain is not not HIN secured, it will appear in the
Note
Ensure that all relay host and domain configurations are correct before proceeding.
Once the configuration has been reviewed and completed, click "Save" to continue.
Settings page¶
On this page under Settings menu, configure your global mail transport settings for the secure mail relay setup which are common for the whole instance. Detailed configuration for each domain can be performed under Domains -> $domain
The following settings are available Settings menu:
| Setting | Description |
|---|---|
| Mail server host name | The FQDN of this mail gateway instance (e.g. mail.example.com). |
| Mail server IP addresses | The public IP address(es) of this server. Add additional IPs if the server is reachable on multiple addresses. |
| DNS server | DNS of the host which will be used to resolve MX and other DNS records. Please make sure that the entered DNS server is reachable from the HIN Gateway VM. |
| Default inbound relay | The SMTP relay that receives inbound mail for the selected domain. You can define routing rules based on the sender's domain (external domain) to control where incoming messages are delivered |
| Default outbound relay | The SMTP relay for outbound mail for the selected domain. After the gateway processes a message, it forwards it to this server as the next hop, based on the configured local domain. Migration note: this setting corresponds to Forwarding server in the old MGW. |
Step 15 - Configure whitelist headers¶
Info
For multi-domain perform the operation for each active domain.
Click "Domains" -> "Select domain", then select "Whitelist headers".
Enter the key exactly as configured in the mail server.
Step 16 - Peer certificates¶
Peer certificates are issued by the HIN Certification Authority (HIN CA) for enabled domains.
Once the onboarding is complete, navigate to the Peer certificates section in the dashboard and click the "Sync certificates" button to synchronize your peer certificates from the HIN CA.
Step 17 - Validate peer certificates¶
Ensure that your domain has received its policy-based peer certificate at the bottom of "Domains" -> "domainName" . The status of each domain must be "Good".
Question
Contact HIN Support by email or phone (support@hin.ch / 0848 830 740) if you encounter any issues.
Step 18 - Configure mail server and HIN Gateway¶
If you followed the recommended approach by exporting the private key, importing it into the HIN Gateway, and keeping the same IP address as the existing MGW, no changes are required on the email server.
Otherwise, configure your mail server or the associated components so that traffic is routed via the new HIN Gateway. Check and update the following settings, if required:
Email server¶
- SMTP relay / smart host
- Connectors
- Transport rules
- Routing domains
See Exchange Integration for detailed instructions.
HIN Gateway config¶
Domains page¶
Info
For multi-domain migration perform the operation for each active domain.
Under the Domains menu, for each available domain you can configure specific transport route:
| Setting | Description |
|---|---|
| Inbound relay | The SMTP relay that receives inbound mail for the selected domain. You can define routing rules based on the sender's domain (external domain) to control where incoming messages are delivered |
| Outbound relay | The SMTP relay for outbound mail for the selected domain. After the gateway processes a message, it forwards it to this server as the next hop, based on the configured local domain. Migration note: this setting corresponds to Forwarding server in the old MGW. |
| Trusted networks | Additional networks allowed to relay through this gateway. For more information please check on "Step 18 - Configure mail server" |
| Configure TLS | TLS certificate settings for SMTP connections and from the Generate TLS certificate button you can generate TLS certificate |
| Email authentication | For all settings under Email authentication section please refer to Email authentication (DKIM ARC SPF DMARC) section |
- Note: for migration scenario: Go to the page for each domain, add a Outbound host using the value you recorded from the MGW's
Forwarding serverfield in "Step 1.2 - Backing up the existing MGW".
-
If you are using Microsoft 365 / Exchange Online, add its published outbound IP ranges to
Trusted networksso the HIN Gateway trusts and relays mail coming from Exchange Online:
Settings page¶
On this page under Settings menu, configure your global mail transport settings for the secure mail relay setup which are common for the whole instance. Detailed configuration for each domain can be performed under Domains -> $domain
The following settings are available Settings menu:
| Setting | Description |
|---|---|
| Mail server host name | The FQDN of this mail gateway instance (e.g. mail.example.com). |
| Mail server IP addresses | The public IP address(es) of this server. Add additional IPs if the server is reachable on multiple addresses. |
| DNS server | DNS of the host which will be used to resolve MX and other DNS records. Please make sure that the entered DNS server is reachable from the HIN Gateway VM. |
| Default inbound relay | The default SMTP relay that receives inbound mail for the selected domain. You can define routing rules based on the sender's domain (any external domain) to control where incoming messages are delivered. Its fallback to for all domains. |
| Default outbound relay | The default SMTP relay for outbound mail. After the gateway processes a message, it forwards it to this server as the next hop. Its fallback to for all domains |
Step 19 - Test and validate¶
Outgoing:
- Verify that the mail server is configured to send emails to the HIN Gateway using an SMTP relay or Exchange connector.
- Verify that the HIN Gateway can send emails to recipients outside the HIN Community.
- Verify that the HIN Gateway can send emails to recipients inside the HIN Community via WireGuard.
- Send an email from the HIN Community to an external email address (for example, Bluewin, Gmail, Yahoo, or GMX) with (confidential) included in the subject line, and verify that it is delivered successfully.
(Confidential) messages outside of HIN cmmmunity
During the test and validation, if you use Outlook to send confidential emails outside the HIN community using one of the following methods: * Using a specific email template * Using the Key Lock symbol
Please follow the instructions below: * If you are using a specific email template, please ensure that the template is updated so that secure emails are processed correctly through the HIN Gateway. * If you are using the Key Lock symbol, please temporarily use “(Confidential)” in the subject field instead.
HIN is currently developing support for the Outlook integration. This functionality is planned to be made available in a future release. After making the required changes, please test the mail flow again and confirm that the email is processed and delivered successfully after the migration.
Incoming:
- Verify that encrypted emails can be received from the HIN Community via WireGuard. A sender from the
hin.chdomain is the easiest test path. - Verify that encrypted emails can be received from the HIN Community via SMTP using S/MIME.
- Verify that replies from senders outside the HIN Community to an initial secure email (HIN Mail-SEAL) can reach the HIN Gateway.
- Verify that plain-text emails can be received from external senders outside the HIN Community.
- Send an email from an external email address to the HIN Community and verify that it is received successfully.
Confirm:
- Emails are delivered successfully in both directions between the HIN trusted domain and external email addresses.
- Encryption is applied where required.
- There are no unexpected delays or bounces.
- Logging is successful.
Complete the Acceptance Report and return it to your HIN representative.
Step 20 - Change the password of the VM¶
Please make sure that the VM credentials which were provided to you initially are changed to your own defined password, and keep them in a secured and safe place.
Step 21 - Take existing MGW out of service¶
Info
This step applies only for single and multi domain migrations
Warning
Do not delete the existing MGW VM immediately - keep it safe until everything is up and running.
- Ensure there is no active traffic - check:
- No domains are pointing to the MGW (DNS, SMTP, connectors).
- No emails are being forwarded via the old appliance.
- Archive logs - export and save:
- Email logs
- Security/audit logs
- Required for compliance and troubleshooting
- Clean-up (optional) - remove:
- Firewall rules
- DNS entries
- Routing configurations that reference the existing MGW
Annex 1 - Backing up and restoring the appliance settings¶
Info
This step applies only for single and multi domain migrations
To back up or restore the settings of your HIN appliance, click the "Administration" menu in the web administration portal.
Backing up settings¶
Before you create a backup of the current HIN appliance settings, you must set a backup password. This password is required if you need to restore the backup later.
- To set or change the backup password, click "Change Password".
- To create and download a backup file, click "Download".
Changing the backup password¶
To change the password for future backups, click "Change Password".
Note
The new password only applies to backups created after the password has been changed. Existing backup files remain protected by the password that was set when they were created.
Restoring settings¶
To restore appliance settings from a backup file, click "Import Backup File...".
In the dialog window, select the required backup file and enter the password associated with that backup. The appliance settings will then be restored from the selected backup file.
Backup using SCP¶
The MGW supports backing up the appliance via SCP.
To use this option, the public key of the system that will access the MGW must be stored under "Backup using SCP". The backup is generated automatically every day at midnight and is stored on the MGW as backup.tgz.
Using the configured public key, the backup file can be retrieved via SCP with the operating system user backup. A typical SCP command to retrieve the backup file is:
This command downloads the file backup.tgz from the MGW to the current local directory.
Note
If you enter a new public key, the existing key will be replaced.
Multi-Domain Scenarios and Recommendation¶
MGW → HIN Gateway – Mail-Flow Architecture, Phased Rollout, and Rollback Plan
Phase 1 – Starting Point: Baseline (All Domains on MGW)¶
Baseline state
- All domains are routed through the MGW. Example: domain1.ch, domain2.ch, domain3.ch, un-domain1.ch, un-domain2.ch
- The HIN Gateway is deployed and prepared, but no live traffic is routed through it yet.
- DNS MX/SPF records still point to "Public IP A" (MGW), where the MGW is the front-facing gateway or the last MTA.
Pre-flight checklist
- Establish a baseline for the current MGW capacity and mail-flow logs.
- Validate HIN Gateway connectivity to Online Protect, Exchange Online, and the on-premises e-mail server.
- Align stakeholders on the migration schedule and communication plan.
- Review firewall/port documentation ahead of Public IP B provisioning (Phase 2, step 1)
Phase 2 – Migration: Gradual Domain-by-Domain Example¶
Migration steps
- Provision HIN Gateway — assign
Public IP Band update firewall rules (see network documentation for required ports) - Create two connectors in Exchange Online—one inbound and one outbound—pointing to the HIN Gateway.
- Add a mail-flow rule that routes by domain: domain1.ch → HIN Gateway, all remaining domains stay on MGW
- Repeat the process gradually, moving one additional domain at a time until all domains are routed through the HIN Gateway.
Roll-back (per domain)
- Point the affected domain's mail-flow rule back to MGW
- Leave the Stargate connectors in place for the next attempt
- Keep an ordered change log of every connector and rule modification. A rollback must apply these changes in reverse order.
Watch for customer-specific headers
Some domains rely on custom X-headers (routing, anti-spam allow-lists, compliance tags). Confirm that the HIN Gateway connectors preserve or replicate these headers before migrating a domain. Missing headers may result in incorrect routing or rejected e-mails.
Phase 3 Final - fully migrated to HIN Gateway¶
Warning
After successful testing, please keep the new HIN Gateway on Public IP B and do not change this IP address. Changing the public IP will break the secure tunnel, causing the gateway to stop receiving certificate updates.
End state
- All domains now flow through HIN Gateway
- MGW carries no production traffic
- DNS/SPF records now point to Public IP B where the HIN Gateway is the front-facing gateway or the last MTA.
Clean-up checklist
- Remove the old MGW connectors and mail-flow rules
- Decommission the MGW VM once monitoring confirms that it is receiving no traffic and that e-mail flow is functioning correctly
- Release Public IP A if no longer required
- Update runbooks and DNS documentation
Recommended Migration Approach by Number of Domains¶
Customers with 3 Domains or Fewer
- HIN recommends migrating all domains at once.
- HIN will support the customer in successfully migrating the first domain.
- Once the first domain has been migrated successfully, the customer can migrate the remaining domains independently.
- This approach keeps the migration simple and avoids the need for a parallel environment.
Customers with More Than 3 Domains
- HIN recommends setting up a parallel environment alongside the existing MGW.
- Domains can then be migrated gradually to the new environment.
- HIN will support the customer in successfully migrating the first domain.
- After the first successful migration, the customer can decide how and when to move the remaining domains to the new environment.
Warning
Confirm the exact firewall ports and connector settings against the current network documentation before performing any installation steps.
Note
See the specific notes in the HIN Gateway Installation Guide regarding multi-domain migration.
Technical configuration and Integration
Applications overview¶
Applications¶
- smimekeys-client - S/MIME keys client service (port
8081) - policy - Policy service (port
8082) - irisagent - IRIS Agent service (port
8083, WireGuard:19818/udp,19818/tcp) - mxengine - MX Engine service (port
8084, SMTP:1587) - stalwart - Stalwart MTA mail server (port
25,10026) - clamav - ClamAV antivirus; scans mail at Stalwart's SMTP DATA stage via the milter protocol (port
7357) - mtaconf - MTA configuration daemon (API:
8080) - dashboard - Web-based admin UI for onboarding, domain management, and monitoring (port
443) - policy-sync - Syncs OPA/Rego policies from Git repository to database (runs continuously)
Infrastructure¶
- PostgreSQL - Database (port
5432) - Vault - Secrets management (internal port
8200, not published to the host) - MinIO - S3-compatible storage (API on host port
9000; console not published to the host) - Keycloak - Identity provider and OIDC authentication (port
8180) - APISIX - API gateway with OIDC bearer auth (port
9080) - NATS - Inter-service messaging (triggers Stalwart reloads from dashboard)
Init Containers¶
- vault-init - Initializes and unseals Vault on first run
- seaweedfs-init - Creates the S3 bucket
- apisix-init - Generates APISIX config from template
- keycloak-init - Sets initial admin password
Monitoring¶
- node-exporter - Host metrics for Prometheus (port
9100) - version-collector - Collects app versions from
/livenessendpoints for node-exporter - Alloy - Log collector for Loki (ships app logs)
- Dozzle - Real-time container log viewer (port
8190, HTTPS, behind Keycloak SSO via oauth2-proxy; optional, enabled withDOZZLE_ENABLED) - oauth2-proxy - OIDC relying party that authenticates Dozzle access against Keycloak (starts together with Dozzle)
See Monitoring and Logs for detailed configuration and usage.
Architecture overview¶
VM Architecture overview¶
Monitoring and Logs¶
Stargate includes built-in monitoring and log collection services that run alongside the application containers.
Components¶
| Service | Port | Purpose |
|---|---|---|
| node-exporter | 9100 |
Host-level metrics (CPU, memory, disk, network) for Prometheus |
| version-collector | - | Collects app versions from /liveness endpoints |
| Alloy | 12345 |
Docker log collector - ships container logs to Loki |
| Loki | 3100 (internal) |
Local log aggregation backend |
| Dozzle | 8190 |
Web-based container log viewer (HTTPS, Keycloak SSO; optional) |
| oauth2-proxy | 8190 |
OIDC relying party that authenticates Dozzle access (with Dozzle) |
Dozzle - Local Log Viewer¶
Dozzle provides a web-based UI to view real-time logs from all Stargate containers. It is optional and enabled by setting DOZZLE_ENABLED="true" in customer-config.sh.
Access is protected by Keycloak: an oauth2-proxy sits in front of Dozzle and requires the same login as the dashboard (the stargate realm). Dozzle itself is not exposed directly.
Access: open https://<SERVER_IP>:8190 in a browser and sign in with your HIN Gateway (Keycloak) credentials.
Note
Port 8190 (HTTPS) must be reachable from your network. If you restrict access by IP or firewall, allow 8190/tcp as you do for the dashboard and Keycloak.
Logs are organized by service. By selecting a specific service, you can view its corresponding log entries and details.
Grafana Alloy - Log Forwarding¶
Grafana Alloy collects logs from all Stargate application containers and writes them to the local Loki instance. Optionally, logs can also be forwarded to a remote Loki-compatible endpoint for centralized monitoring.
How it works¶
- Alloy discovers Stargate containers via the Docker socket
- Logs are always written to the local Loki instance (used by the dashboard for log export)
- If a remote Loki URL is configured, logs are additionally forwarded to that endpoint
Configuring remote log forwarding¶
From the HIN Gateway dashboard, navigate to the Settings page. Under the Grafana Alloy section, enter the Loki push URL of your remote log collection server:
The URL should follow the standard Loki push API format:
Leave the field blank to disable remote log forwarding.
Note
Changes take effect within 1 minute (Alloy polls the dashboard configuration on that interval). No container restart is required.
Requirements on the remote side¶
Your remote Loki endpoint must be reachable from the Stargate server over HTTPS (port 443). If you use IP-based allowlisting on your ingress, add the Stargate server's public IP.
Prometheus Metrics¶
Stargate exposes Prometheus-compatible metrics endpoints from its application containers. These can be scraped by any Prometheus-compatible server for centralized metrics collection.
Available endpoints¶
| Service | Port | Path |
|---|---|---|
| smimekeys-client | 2113 |
/metrics |
| irisagent | 2114 |
/metrics |
| policy | 2115 |
/metrics |
| mxengine | 2116 |
/metrics |
| node-exporter | 9100 |
/metrics |
| APISIX | 9091 |
/apisix/prometheus/metrics |
Scrape configuration¶
Add the Stargate server as a target in your Prometheus configuration. Example for a single instance:
scrape_configs:
- job_name: 'stargate-<name>-smimekeys'
static_configs:
- targets: ['<STARGATE_IP>:2113']
labels:
environment: 'stargate-<name>'
service: 'smimekeys-client'
metrics_path: /metrics
- job_name: 'stargate-<name>-irisagent'
static_configs:
- targets: ['<STARGATE_IP>:2114']
labels:
environment: 'stargate-<name>'
service: 'irisagent'
metrics_path: /metrics
- job_name: 'stargate-<name>-policy'
static_configs:
- targets: ['<STARGATE_IP>:2115']
labels:
environment: 'stargate-<name>'
service: 'policy'
metrics_path: /metrics
- job_name: 'stargate-<name>-mxengine'
static_configs:
- targets: ['<STARGATE_IP>:2116']
labels:
environment: 'stargate-<name>'
service: 'mxengine'
metrics_path: /metrics
- job_name: 'stargate-<name>-node'
static_configs:
- targets: ['<STARGATE_IP>:9100']
labels:
environment: 'stargate-<name>'
service: 'node-exporter'
metrics_path: /metrics
Replace <STARGATE_IP> with the server's public or private IP and <name> with a deployment identifier (e.g., prod, customer-name).
Tip
The environment and service labels enable filtering in Grafana dashboards across multiple Stargate instances.
Firewall requirements¶
The metrics ports (2113-2116, 9091, 9100) carry no authentication. Restrict them so they are reachable only from your Prometheus server, and never from the internet.
If you allow access by IP, add your monitoring server's IP and nothing else.
Node Exporter¶
The node-exporter service exposes standard host-level metrics (CPU, memory, disk I/O, network) on port 9100. It also includes a textfile collector that exposes custom metrics from the version-collector sidecar (application version information).
Summary of exposed ports¶
| Port | Service | Protocol | Purpose |
|---|---|---|---|
8190 |
Dozzle (via oauth2-proxy) | HTTPS | Authenticated log viewer UI (Keycloak SSO) |
9100 |
node-exporter | HTTP | Host metrics (Prometheus) |
2113 |
smimekeys-client | HTTP | App metrics (Prometheus) |
2114 |
irisagent | HTTP | App metrics (Prometheus) |
2115 |
policy | HTTP | App metrics (Prometheus) |
2116 |
mxengine | HTTP | App metrics (Prometheus) |
9091 |
APISIX | HTTP | Gateway metrics (Prometheus) |
Exchange Integration with HIN Gateway¶
This guide explains how to configure Microsoft Exchange (Online and On-Premises) to route mail through the HIN Gateway gateway for S/MIME signing and encryption.
Overview¶
HIN Gateway acts as a mail relay between external mail servers and your Exchange environment. Two integration patterns are supported:
Pattern A - Exchange Online as primary MX with transport rules:
flowchart LR
I1 --> EO --> TR --> C --> S1 --> EO
E2 --> TR2 --> OC --> S2 --> I2
I1["Internet"]
I2["Internet"]
EO["Exchange Online"]
E2["Exchange Online"]
TR["Transport Rule"]
TR2["Transport Rule"]
OC["Outbound Connector"]
C["Connector"]
S1["HIN Gateway"]
S2["HIN Gateway"]
Pattern B - HIN Gateway as primary MX:
flowchart LR
I1 --> mx15 --> mx20
EO --> TR --> OC --> HG --> I2
I1["Internet"]
I2["Internet"]
mx15["HIN Gateway (MX priority 15)"]
mx20["Exchange Online (MX priority 20)"]
EO["Exchange Online"]
TR["Transport Rule"]
OC["Outbound Connector"]
HG["HIN Gateway"]
In both patterns, you need:
- DNS records pointing to the HIN Gateway server
- Outbound connector - routes mail from Exchange to HIN Gateway
- Inbound connector - accepts mail from HIN Gateway into Exchange
- Transport rule - triggers the outbound connector for external recipients
Prerequisites¶
Before configuring Exchange, ensure:
- HIN Gateway is installed and running (deployment instructions)
- You have the HIN Gateway server's public IP address (referred to as
<HIN Gateway_IP>below) - You have the mail hostname of the HIN Gateway server (referred to as
<MAIL_HOSTNAME>, e.g.mail.example.com) - You know your mail domain (referred to as
<YOUR_DOMAIN>, e.g.example.com) - You have Exchange admin access (Exchange Admin Center or on-premises Exchange Management Shell)
- DNS records are configured per the DNS Setup Guide (A, MX, SPF at minimum)
Part 1: DNS Setup¶
See the DNS Setup Guide for complete instructions on configuring A, MX, SPF, PTR, DMARC, and DKIM records.
At minimum, before proceeding with the Exchange configuration below, you need:
- A record:
<MAIL_HOSTNAME>pointing to<HIN Gateway_IP> - MX record:
<YOUR_DOMAIN>with HIN Gateway at higher priority (lower number) than Exchange - SPF record:
ip4:<HIN Gateway_IP>andip4:<HIN_SEALER_IP>added to your domain's TXT record (see DNS Setup Guide - SPF for sealer IPs)
Part 2: Exchange Online Configuration¶
Step A: Create the Outbound Connector (Office 365 → HIN Gateway)¶
This connector routes outbound mail from Exchange Online to the HIN Gateway relay server.
-
Navigate to the Exchange Admin Center - Connectors
-
Click "+ Add a connector"
-
Connection from: Select "Office 365"
- Connection to: Select "Your organization's email server"
-
Click "Next"
-
Connector name: Enter a descriptive name, e.g.:
- Check "Retain internal Exchange email headers"
-
Click "Next"
-
Use of connector: Select "Only when I have a transport rule set up that redirects messages to this connector"
- Click "Next"
Tip
This is important - the connector won't route any mail by itself. It will only be used when triggered by the transport rule created in Step C.
- Routing: Select "Route email through these smart hosts"
- Enter the HIN Gateway server IP address:
<HIN Gateway_IP> -
Click "+" to add it, then click "Next"
-
Security restrictions: Select "Any digital certificate, including self-signed certificates"
- Click "Next"
Note
HIN Gateway's MTA (Stalwart) accepts opportunistic TLS on inbound connections. Selecting "any digital certificate" ensures connectivity even with self-signed certificates.
- Validation email: Enter a valid email address for your domain (e.g.
user@<YOUR_DOMAIN>) - Click "+", then click "Validate"
- Wait for validation to complete, then click "Next"
Tip
For validation to succeed, the HIN Gateway server must be running and accepting mail on port 25.
-
Review the settings and click "Create connector"
-
On the confirmation screen, click "Done"
Step B: Create the Inbound Connector (HIN Gateway → Office 365)¶
This connector accepts mail from the HIN Gateway relay server into Exchange Online.
-
From the Connectors page, click "+ Add a connector"
-
Connection from: Select "Your organization's email server"
- Connection to: Shows "Office 365" (automatic)
-
Click "Next"
-
Connector name: Enter a descriptive name, e.g.:
- Check "Retain internal Exchange email headers"
-
Click "Next"
-
Authenticating sent email: Select "By verifying that the IP address of the sending server matches one of the following IP addresses that belong exclusively to your organization"
- Enter the HIN Gateway server IP address:
<HIN Gateway_IP> - Click "+" to add it, then click "Next"
Note
This tells Exchange Online to trust mail from this specific IP address, bypassing additional spam/authentication checks for mail that has already been processed by HIN Gateway.
-
Review the settings and click "Create connector"
-
Click "Done"
Verify Connectors¶
After creating the connectors (including the Send to MX connector from Step D), the Connectors page should show:
| Status | Name | From | To |
|---|---|---|---|
| On | Receive mail from HIN Gateway relay server | Your org | O365 |
| On | From Office 365 to HIN Gateway relay server | O365 | Your org |
| On | Send to MX | O365 | Your org |
Step C: Create the Transport Rule¶
The transport rule redirects all outbound mail through the HIN Gateway outbound connector, except mail originating from HIN Gateway itself (to prevent mail loops).
-
Navigate to Exchange Admin Center - Rules
-
Click "+ Add a rule" → "Create a new rule"
-
Rule name: Enter a descriptive name, e.g.:
- Apply this rule if: Select "The recipient..." → "is external/internal" → "Outside the organization"
- Click "Save"
Note
This condition ensures only outbound mail (to external recipients) is redirected through HIN Gateway.
- Do the following: Select "Redirect the message to..." → "the following connector" → select the outbound connector created in Step A (e.g. "From Office 365 to HIN Gateway relay server")
-
Click "Save"
-
Except if: Click "+" to add an exception
- Select "The sender..." → "IP address is in any of these ranges"
- Enter the HIN Gateway server IP address:
<HIN Gateway_IP> - Click "Add", verify the IP is listed, then click "Save"
Warning
This exception is critical - it prevents mail loops. Without it, mail from HIN Gateway arriving at Exchange Online would be redirected back to HIN Gateway in an infinite loop.
- Review the rule summary. It should show:
- Apply this rule if: The recipient is located Outside the organization
- Do the following: Redirect the message to the connector "From Office 365 to HIN Gateway relay server"
-
Except if: The sender IP address is in one of these ranges:
<HIN Gateway_IP> -
Click "Next", then "Next" again, then "Finish", then "Done"
-
Enable the rule: The rule is created in a disabled state. Click on the rule in the list and toggle "Enable or disable rule" to "Enabled"
Tip
Do not forget to enable the rule - it will not work until enabled.
Step D: Outgoing to MX (Send to MX Connector and Rule)¶
After HIN Gateway has processed (signed or encrypted) an outbound message, it hands the message back to Exchange Online through the inbound connector from Step B. Exchange Online then has to deliver it to the recipient's mail server. The "Outgoing to MX" setup does this: a dedicated Send to MX connector sends the message to the MX of the external recipient, and the outgoing_to_mx transport rule redirects the processed messages to that connector.
Create the Send to MX Connector (Office 365 → Partner Organization)¶
-
From the Connectors page, click "+ Add a connector"
-
Connection from: Select "Office 365"
- Connection to: Select "Partner organization"
-
Click "Next"
-
Connector name: Enter a descriptive name, e.g.:
- Make sure the connector is turned on
- Click "Next"
Note
The connector name can be chosen freely. If you use a different name, select that name in the outgoing_to_mx rule below.
- Use of connector: Select "Only when I have a transport rule set up that redirects messages to this connector"
- Click "Next"
Tip
Like the outbound connector from Step A, this connector does not route any mail by itself. It is only used when the outgoing_to_mx rule below redirects a message to it.
- Routing: Select "Use the MX record associated with the partner's domain"
-
Click "Next"
-
Security restrictions: Leave "Always use Transport Layer Security (TLS) to secure the connection" unchecked, so that no security restrictions apply
- Click "Next"
Note
This connector delivers mail to any external recipient. Requiring TLS or a specific certificate here would block delivery to recipient servers that do not meet those requirements.
- Validation email: Enter an external email address (outside your organization)
- Click "+", then click "Validate"
-
Wait for validation to complete, then click "Next"
-
Review the settings and click "Create connector"
-
Click "Done"
The connector summary should show:
- Mail flow scenario: From Office 365, to Partner organization
- Name: Send to MX
- Status: On
- Use of connector: Use only when I have a transport rule set up that redirects messages to this connector
- Routing: Use the MX record associated with the partner's domain
- Security restrictions: None
Create the outgoing_to_mx Transport Rule¶
This rule routes outbound messages that come back from HIN Gateway to the Send to MX connector. It recognizes these messages by the outgoing message header.
-
Navigate to Exchange Admin Center - Rules
-
Click "+ Add a rule" → "Create a new rule"
-
Rule name: Enter:
- Apply this rule if: Select "The message headers..." → "matches these text patterns"
- Header name:
outgoing - Text pattern:
outgoing_<YOUR_DOMAIN>(e.g.outgoing_example.com) - Click "Save"
Note
The outgoing header must already be on the message when it returns from HIN Gateway. In the HIN reference setup it is added by a separate rule (set_header) that applies when the sender is located inside the organization and sets the message header outgoing to the value outgoing_<YOUR_DOMAIN>. Without this header, the outgoing_to_mx rule never matches.
- Do the following: Select "Redirect the message to" → "the following connector" → select "Send to MX"
-
Click "Save"
-
Except if: Leave empty
-
Click "Next" to open the rule settings:
- Rule mode: "Enforce"
- Severity: "Not specified"
- Leave "Activate this rule on" and "Deactivate this rule on" unchecked
- Check "Stop processing more rules"
- Leave "Defer the message if rule processing doesn't complete" unchecked
-
Match sender address in message: "Header"
-
Click "Next", then "Finish", then "Done"
-
Set the priority: Click on the rule in the list, click "Edit rule settings" and set Priority to a higher priority (lower number) than the transport rule from Step C, e.g.
1 - Click "Save"
Warning
The priority and "Stop processing more rules" are critical. The rule must be evaluated before the Step C rule and must stop further processing. Otherwise a message returning from HIN Gateway can be redirected to HIN Gateway again instead of being delivered to the recipient.
-
Review the rule details. They should show:
- Rule name: outgoing_to_mx
- Mode: Enforce
- Severity: Not specified
- Set date range: Specific date range is not set
- Senders address: Matching Header
- Priority: 1
- For rule processing errors: Ignore
- Apply this rule if:
'outgoing'header matches the following patterns:'outgoing_<YOUR_DOMAIN>' - Do the following: Route the message using the connector named "Send to MX" and Stop processing more rules
-
Enable the rule: The rule is created in a disabled state. Toggle "Enable or disable rule" to "Enabled"
Tip
Do not forget to enable the rule - it will not work until enabled.
Part 3: On-Premises Exchange Server Configuration¶
For on-premises Exchange Server (2016, 2019), the setup is similar but configured through the Exchange Management Console (EAC) or Exchange Management Shell (PowerShell).
Send Connector (On-Premises → HIN Gateway)¶
Create a Send connector to route outbound mail through HIN Gateway:
Exchange Management Shell (PowerShell):
New-SendConnector -Name "To HIN Gateway Relay" `
-AddressSpaces "SMTP:*;1" `
-SmartHosts "<HIN Gateway_IP>" `
-SmartHostAuthMechanism None `
-DNSRoutingEnabled $false `
-SourceTransportServers "<YOUR_EXCHANGE_SERVER>"
Exchange Admin Center (GUI):
- Navigate to Mail flow → Send connectors
- Click + to create a new connector
- Name: "To HIN Gateway Relay"
- Type: Select "Internet"
- Network settings: Select "Route mail through smart hosts", add
<HIN Gateway_IP> - Smart host authentication: Select "None"
- Address space: Add
*(all domains) or specific external domains - Source server: Select your Exchange transport server(s)
Receive Connector (HIN Gateway → On-Premises)¶
Create or modify a Receive connector to accept mail from HIN Gateway:
Exchange Management Shell (PowerShell):
New-ReceiveConnector -Name "From HIN Gateway Relay" `
-Bindings "0.0.0.0:25" `
-RemoteIPRanges "<HIN Gateway_IP>" `
-TransportRole FrontendTransport `
-Usage Custom `
-AuthMechanism ExternalAuthoritative `
-PermissionGroups ExchangeServers
Exchange Admin Center (GUI):
- Navigate to Mail flow → Receive connectors
- Click + to create a new connector
- Name: "From HIN Gateway Relay"
- Type: Select "Frontend Transport"
- Network adapter bindings: Leave default or bind to specific IP
- Remote network settings: Remove the default
0.0.0.0-255.255.255.255and add only<HIN Gateway_IP> - Authentication: Check "Externally Secured"
- Permission groups: Check "Exchange servers"
Transport Rule (On-Premises)¶
Create a transport rule to redirect outbound mail through the Send connector:
Exchange Management Shell (PowerShell):
New-TransportRule -Name "Relay outbound via HIN Gateway" `
-SentToScope NotInOrganization `
-RouteMessageOutboundConnector "To HIN Gateway Relay" `
-ExceptIfSenderIpRanges "<HIN Gateway_IP>"
Exchange Admin Center (GUI):
- Navigate to Mail flow → Rules
- Click + → "Create a new rule"
- Name: "Relay outbound via HIN Gateway"
- Apply this rule if: "The recipient is located..." → "Outside the organization"
- Do the following: "Redirect the message to..." → "the following connector" → "To HIN Gateway Relay"
- Except if: "The sender IP address is in..." → add
<HIN Gateway_IP>
Part 4: HIN Gateway-Side Configuration¶
Automatic Configuration (Default)¶
By default, Stalwart automatically discovers where to deliver processed mail by looking up MX records for each domain configured via the dashboard's /mail page. It filters out its own hostname and uses the remaining MX entries as delivery targets.
This works when:
- Your domain has MX records pointing to both HIN Gateway and Exchange
- HIN Gateway has a higher-priority (lower number) MX record than Exchange
Manual Override via the dashboard¶
If you want all outbound mail from HIN Gateway to go to a single Exchange endpoint (e.g. Exchange Online Protection), set the relay host through the dashboard's /mail page (e.g. [smtp.office365.com]). The dashboard sends the value to mtaconf's REST API and the daemon applies it to Stalwart.
Note
A single relay host sends all mail through one server and does not support per-domain routing. For multiple domains routing through different Exchange servers, use the per-domain relay map on the same dashboard page (configures sender_dependent_relayhost_maps under the hood) - see Multi-Domain Setup below.
Multi-Domain Setup¶
For setups with multiple domains and different Exchange servers (e.g. BALZ Informatik AG with 26 domains), use MX records for per-domain routing:
domain1.com MX 10 exchange1.domain1.com
domain1.com MX 20 HINGateway.domain1.com
domain2.com MX 10 exchange2.domain2.com
domain2.com MX 20 HINGateway.domain2.com
Each domain's MX records tell HIN Gateway where to deliver processed mail for that specific domain.
Verify HIN Gateway Configuration¶
After setup, verify the Stalwart configuration:
Check relay configuration¶
Check mail queue (should be empty when everything is working)¶
Send a test email and check logs¶
Troubleshooting¶
Mail not leaving Exchange Online¶
- Verify the transport rule is enabled (it is created in a disabled state)
- Check the rule conditions - it should apply to recipients "Outside the organization"
- Verify the outbound connector validation passed
- Check Exchange message trace in the Admin Center for delivery status
Mail loops (duplicate messages)¶
- Ensure the transport rule has the exception for the HIN Gateway IP address
- Without this exception, mail from HIN Gateway arriving at Exchange gets redirected back to HIN Gateway
HIN Gateway not accepting mail from Exchange¶
- Check port 25 is open on the HIN Gateway server's firewall
- Verify SPF record includes the HIN Gateway IP
- Check Stalwart logs:
docker logs stargate-stalwart
Exchange Online rejecting mail from HIN Gateway¶
- Verify the inbound connector is configured with the correct HIN Gateway IP
- Check that the HIN Gateway IP hasn't changed
- Verify the connector is enabled (Status: On)
TLS Certificate Errors¶
HIN Gateway uses opportunistic TLS with a self-signed certificate. The outbound connector in Exchange should be configured to accept "Any digital certificate, including self-signed certificates". If you see TLS-related errors:
- Verify the outbound connector security setting allows self-signed certificates
- For on-premises Exchange, ensure the Send connector does not require TLS (
-RequireTLS $false)
Validation fails during connector creation¶
The outbound connector validation requires:
- HIN Gateway server is running and accepting connections on port 25
- The validation email address is valid for your domain
- Network path between Exchange Online and HIN Gateway is open (no firewall blocking)
Quick Reference¶
| Component | Exchange Online Location | Purpose |
|---|---|---|
| Outbound Connector | Admin Center → Mail flow → Connectors | Route outbound mail to HIN Gateway |
| Inbound Connector | Admin Center → Mail flow → Connectors | Accept mail from HIN Gateway |
| Transport Rule | Admin Center → Mail flow → Rules | Trigger outbound connector for external recipients |
| Send to MX Connector | Admin Center → Mail flow → Connectors | Deliver mail returning from HIN Gateway to the recipient's MX |
| outgoing_to_mx Rule | Admin Center → Mail flow → Rules | Route mail returning from HIN Gateway to the Send to MX connector |
| DNS Record | Example | Purpose |
|---|---|---|
| A | mail IN A <HIN Gateway_IP> |
Point hostname to HIN Gateway |
| MX (HIN Gateway) | @ IN MX 15 mail.<YOUR_DOMAIN>. |
Inbound mail hits HIN Gateway first |
| MX (Exchange) | @ IN MX 20 <DOMAIN>.mail.protection.outlook.com. |
Fallback / delivery target |
| SPF | ip4:<HIN Gateway_IP> and ip4:<HIN_SEALER_IP> added to existing TXT record |
Authorize HIN Gateway and HIN sealer to send mail |
For the full DNS setup (including PTR, DMARC, DKIM, and multi-domain), see the DNS Setup Guide.
Stargate mail relay setup¶
Create a Stargate relay for a mail domain hosted in Microsoft Office 365¶
For the relay, we need a VM or a server with a real static IP address.
In this example we will use a VM with IP address 128.140.117.200 and hostname mail.vrgnservices.eu to relay mail for domain vrgnservices.eu.
Set up DNS records¶
See the DNS Setup Guide for complete instructions on all required records (A, MX, SPF, PTR, DMARC, DKIM).
Quick example for domain vrgnservices.eu with Stargate IP 128.140.117.200:
- A record:
mail.vrgnservices.eu→128.140.117.200 - MX record:
MX @ 15 mail.vrgnservices.eu.(higher priority than the existing Exchange MX at 20) - SPF record:
v=spf1 ip4:128.140.117.200 include:spf.protection.outlook.com -all
Verify:
# host -t mx vrgnservices.eu
vrgnservices.eu mail is handled by 20 vrgnservices-eu.mail.protection.outlook.com.
vrgnservices.eu mail is handled by 15 mail.vrgnservices.eu.
# host -t txt vrgnservices.eu|grep v=spf1
vrgnservices.eu descriptive text "v=spf1 ip4:128.140.117.200 include:spf.protection.outlook.com -all"
Install the Stargate docker compose containers¶
Requirements¶
- 2 CPU cores (minimum)
- 4 GB RAM (minimum)
- 20 GB storage (minimum)
- Root access: Must be run as root or with
sudo - Supported distributions:
- RHEL 8, 9 and 10 compatible distributions such as Alma Linux, Rocky Linux, CentOS Stream
- Ubuntu 22 and 24
- Debian 11, 12 and 13
- Real IPv4 address
- Valid DNS records: Your domain must have:
- MX records pointing to your mail servers
- SPF record defining allowed sending networks
The script installs all components and starts them. Mail domains and the Stalwart hostname are then configured at runtime via the dashboard's /mail page (the mtaconf daemon extracts the necessary mail relay settings from DNS based on those domains).
Set up Exchange¶
We need to configure connectors and a transport rule in Exchange to relay all outgoing mail to the Stargate relay and allow incoming mail from it.
Navigate to https://admin.exchange.microsoft.com/#/connectors
Outgoing connector¶
Create an outgoing mail connector, click "Add":
Select "Connection from": "Office 365" "Connection to": "your organization's email server", click "Next".
Name it something like "From Office 365 to Stargate relay server" and check "Retain Internal Exchange email headers", click "Next".
Select "Only when I have a transport rule set up that redirects messages to this connector", click "Next".
Enter the IP address of the Stargate relay server, click "+", click "Next".
Select "Any digital certificate, including self-signed certificates", click "Next".
Enter a valid email address for your domain, click "+", click "Validate", click "Next".
Click "Create connector".
Click "Add another connector".
Incoming connector¶
Create an incoming mail connector, choose "Connection from": "Your organization's email server", click "Next".
Name it something like "Receive mail from Stargate relay server" and check "Retain internal Exchange email headers", click "Next.
Select "By verifying that the IP address of the sending server matches one of the following IP addresses, type the IP address of the Stargate server, click "+", click "Next".
Click "Create connector".
Click "Done".
This is how it looks when done:
Transport Rule¶
Create the transport rule. Navigate to https://admin.exchange.microsoft.com/#/transportrules
Click "+Add a rule" → "Create a new rule".
Name it something like "Relay all mail to Stargate except mail coming from it", choose "Apply rule if" "The recipient:" "is external/internal" "Outside the organization", click "Save".
Choose "Do the following" "Redirect message to the following connector" "From Office 365 to Stargate relay server", click "Save".
Choose "Except if The sender IP address is in any of these ranges" enter the IP address of the Stargate server, click "Add", check the IP address and click "Save".
This is needed to prevent mail loops, as this rule also applies to other domains hosted in Office 365.
Now it should look like this, click "Next":
Click "Next".
Click "Finish".
Click "Done".
Click on the rule and set the "Enable or disable rule" to "Enabled"
DNS Setup for HIN Gateway¶
This guide covers all DNS records required for a working HIN Gateway deployment. Configure these records before installing HIN Gateway or immediately after, depending on the record type.
Throughout this guide:
<HIN Gateway_IP>- your HIN Gateway server's public static IP address (SERVER_STATIC_IPincustomer-config.sh)<MAIL_HOSTNAME>- the FQDN of the HIN Gateway relay (e.g.mail.example.ch; configured via the dashboard's/mailpage)<YOUR_DOMAIN>- your mail domain (e.g.example.ch; configured via the dashboard's/mailpage)
Record Summary¶
| Record | Name | Value | Required | When |
|---|---|---|---|---|
| A | <MAIL_HOSTNAME> |
<HIN Gateway_IP> |
Yes | Before install |
| MX | <YOUR_DOMAIN> |
<MAIL_HOSTNAME> (priority 15) |
Yes | Before install |
| SPF | <YOUR_DOMAIN> |
ip4:<HIN Gateway_IP> added to TXT |
Yes | Before install |
| PTR | <HIN Gateway_IP> |
<MAIL_HOSTNAME> |
Recommended | Before install |
| DMARC | _dmarc.<YOUR_DOMAIN> |
v=DMARC1; p=none; ... |
Recommended | After install |
| DKIM | selector._domainkey.<YOUR_DOMAIN> |
From M365/provider | Recommended | After install |
For multi-domain deployments, repeat the MX, SPF, DMARC, and DKIM records for each domain listed in MAIL_DOMAINS.
Required Records¶
A Record¶
Create an A record pointing the HIN Gateway mail hostname to the server's public IP:
Example:
If the HIN Gateway has an IPv6 address, add an AAAA record as well:
Why: External mail servers connect to this hostname to deliver mail. Without the A record, the MX record below is unresolvable.
MX Records¶
Add an MX record for the HIN Gateway with a higher priority (lower number) than the existing mail server. This ensures inbound mail reaches the HIN Gateway first for S/MIME processing before being forwarded to Exchange or your mail platform.
Keep the existing Exchange / mail server MX record at a lower priority (higher number):
Example (complete MX set):
Info
The lower MX number means higher priority. HIN Gateway at priority 15 receives mail before Exchange Online at priority 20.
Why: The HIN Gateway intercepts inbound mail, processes S/MIME, then forwards to the next MX (Exchange). The second MX record is also used by Stalwart to know where to relay processed mail.
Important: If the HIN Gateway is the only MX record for a domain, Stalwart will filter out its own hostname and have no delivery target. Always keep a second MX pointing to your actual mail server.
SPF Record¶
Add the HIN Gateway server IP and the HIN sealer IP to your domain's SPF record so outbound mail relayed through it passes SPF checks at the recipient's end.
If you use M365 / Exchange Online:
<YOUR_DOMAIN>. TXT "v=spf1 ip4:<HIN Gateway_IP> ip4:<HIN_SEALER_IP> include:spf.protection.outlook.com -all"
If you do not use M365 / Google Workspace:
Example:
example.ch. TXT "v=spf1 ip4:128.140.117.200 ip4:193.247.208.66 include:spf.protection.outlook.com -all"
Why the HIN sealer IP is required
When the HIN Gateway produces a SEAL'd (encrypted) message for a non-HIN recipient, the final outbound hop to the recipient is the HIN sealer, not your HIN Gateway or M365. Without the sealer IP in your SPF record, every SEAL'd outbound message will fail SPF at the recipient and - because there is no DKIM signature on the SEAL'd payload - DMARC will fail too. Strict-DMARC recipients (Gmail, Outlook with p=reject enforcement, Proofpoint) will reject or junk the message.
Sealer IPs to add in SPF:
HIN publishes more than one sealer host (e.g. mx1, mx2, mx3), include all their IPs. Resolve them with dig +short mx hin.ch followed by dig +short A <each-mx>. Until you have the full list, leave the SPF policy at ~all (softfail) instead of -all (hardfail) so that legitimate SEAL mail through any unlisted sealer IP is not outright rejected.
SPF lookup limit
The total include: chain in an SPF record must stay under 10 DNS lookups. Adding ip4: entries does not count toward this limit. Check your count with MXToolbox SPF lookup.
How the HIN Gateway uses SPF: The mtaconf daemon resolves each domain's SPF record to auto-populate the list of IPs allowed to relay through the HIN Gateway without authentication. This is how Microsoft 365 outbound IPs get whitelisted automatically - they appear in the include:spf.protection.outlook.com chain.
Recommended Records¶
PTR (Reverse DNS)¶
Configure the reverse DNS (PTR) record for the HIN Gateway IP to match <MAIL_HOSTNAME>:
This is configured at your hosting provider (Hetzner, Azure, AWS, etc.), not in your domain registrar's DNS panel. Most providers have a "Reverse DNS" or "rDNS" setting in the server/IP management page.
Why: Many receiving mail servers (including Gmail and Outlook) check that the connecting IP's PTR record resolves to a hostname, and that hostname resolves back to the same IP (forward-confirmed reverse DNS / FCrDNS). A missing or mismatched PTR is a strong spam signal and can cause delivery failures.
DMARC Record¶
Publish a DMARC policy for each sending domain. Start with p=none (monitoring only), then tighten after confirming alignment:
Example:
Once DMARC aggregate reports confirm that SPF and/or DKIM pass consistently, tighten the policy:
p=none- monitoring only (start here)p=quarantine- suspicious mail goes to spamp=reject- unauthorized mail is rejected
Why: DMARC ties SPF and DKIM together and tells recipients what to do with mail that fails both. Even p=none is enough to clear Outlook's "we can't verify this sender" banner, as long as SPF passes.
Check your DMARC record: MXToolbox DMARC lookup
DKIM Records¶
If your domain is an accepted domain in M365 or Google Workspace, enable DKIM signing in the admin centre and publish the CNAME records as instructed:
M365 example:
selector1._domainkey.<YOUR_DOMAIN>. CNAME selector1-<YOUR_DOMAIN_DASHED>._domainkey.<TENANT>.onmicrosoft.com.
selector2._domainkey.<YOUR_DOMAIN>. CNAME selector2-<YOUR_DOMAIN_DASHED>._domainkey.<TENANT>.onmicrosoft.com.
Note
Publishing the CNAME records alone is not enough - DKIM signing must also be enabled in the M365 admin centre (Defender portal > Email authentication > DKIM).
Why: DKIM proves the message body was not tampered with in transit. When combined with SPF and DMARC, it provides the strongest sender authentication.
Multi-Domain Setup¶
For deployments handling multiple mail domains (configured via the dashboard's /mail page), each domain needs its own set of DNS records.
Per-Domain Records¶
For each configured domain:
| Record | Required |
|---|---|
MX pointing to <MAIL_HOSTNAME> |
Yes |
SPF including ip4:<HIN Gateway_IP> |
Yes |
DMARC (_dmarc.<domain>) |
Recommended |
| DKIM (from your mail provider) | Recommended |
The A record and PTR record are shared (they point to the HIN Gateway server, not to individual domains).
Per-Domain Mail Routing¶
Each domain's MX records tell the HIN Gateway where to deliver processed mail. If different domains use different Exchange servers:
domain1.ch MX 15 mail.domain1.ch.
domain1.ch MX 20 exchange1.domain1.ch.
domain2.ch MX 15 mail.domain2.ch.
domain2.ch MX 20 exchange2.domain2.ch.
Alternatively, configure explicit per-domain relay targets via the dashboard's /mail page (relay host field per domain) to override MX-based routing.
Verification¶
After configuring all records, verify them:
# A record
host <MAIL_HOSTNAME>
# Expected: <MAIL_HOSTNAME> has address <HIN Gateway_IP>
# MX records
host -t mx <YOUR_DOMAIN>
# Expected: Both HIN Gateway and Exchange MX records listed
# SPF record
host -t txt <YOUR_DOMAIN> | grep v=spf1
# Expected: SPF record includes ip4:<HIN Gateway_IP>
# PTR (reverse DNS)
host <HIN Gateway_IP>
# Expected: <HIN Gateway_IP> → <MAIL_HOSTNAME>
# Forward-confirmed reverse DNS (FCrDNS)
host $(host <HIN Gateway_IP> | awk '{print $NF}' | sed 's/\.$//')
# Expected: resolves back to <HIN Gateway_IP>
# DMARC
host -t txt _dmarc.<YOUR_DOMAIN>
# Expected: v=DMARC1; p=...
# DKIM (M365)
host -t cname selector1._domainkey.<YOUR_DOMAIN>
# Expected: CNAME to your tenant's onmicrosoft.com
Example output:
$ host mail.example.ch
mail.example.ch has address 128.140.117.200
$ host -t mx example.ch
example.ch mail is handled by 15 mail.example.ch.
example.ch mail is handled by 20 example-ch.mail.protection.outlook.com.
$ host -t txt example.ch | grep v=spf1
example.ch descriptive text "v=spf1 ip4:128.140.117.200 include:spf.protection.outlook.com -all"
$ host 128.140.117.200
200.117.140.128.in-addr.arpa domain name pointer mail.example.ch.
$ host -t txt _dmarc.example.ch
_dmarc.example.ch descriptive text "v=DMARC1; p=none; rua=mailto:postmaster@example.ch"
Online tools:
- MXToolbox MX Lookup
- MXToolbox SPF Check (includes lookup count)
- MXToolbox DMARC Check
- Mail-Tester (send a test mail to get a deliverability score)
Troubleshooting¶
"Client host rejected: Access denied" (554 5.7.1)¶
Stalwart is rejecting the sending server because its IP is not in the allowed relay list. This usually means:
- The SPF record for your domain does not include the sending server's IP range
- The mail configuration has not been reloaded since the SPF record was updated
Reload the mail configuration via the dashboard's /mail page (submit the config again), or restart the container: docker compose restart stalwart
Mail flagged as spam / "can't verify sender"¶
- SPF is missing or does not include the HIN Gateway IP - add
ip4:<HIN Gateway_IP>to your SPF record - DMARC is not published - add at least
v=DMARC1; p=none - PTR record is missing or mismatched - configure reverse DNS at your hosting provider
- DKIM is not enabled in your M365/provider tenant
MX lookup returns only the HIN Gateway¶
If the HIN Gateway is the only MX for a domain, Stalwart filters out its own hostname and has no relay target. Add a second MX record pointing to your mail server:
example.ch. MX 15 mail.example.ch. ← HIN Gateway (inbound)
example.ch. MX 20 example-ch.mail.protection.outlook.com. ← Exchange (relay target)
SPF lookup count exceeded (> 10)¶
Each include: in the SPF record triggers additional DNS lookups. The total chain must stay under 10. Solutions:
- Use
ip4:/ip6:entries instead ofinclude:where possible (they do not count) - Flatten nested includes using a tool like SPF Flattener
- Remove unused
include:entries from old providers
Port 25 blocked by hosting provider¶
Some cloud providers (Azure, certain Hetzner plans) block outbound port 25 by default. Check with your provider and request an exception. This affects both inbound delivery (external servers connecting to your HIN Gateway) and outbound relay (HIN Gateway delivering to MX targets).
Email Authentication (DKIM / ARC / SPF / DMARC)¶
Module: HIN Mail Gateway → Domains → [domain] → Email authentication Applies to: Domain administrators configuring outbound signing and inbound verification for a mail domain
The Email authentication section controls how to proves the authenticity of mail, and how strictly it validates authenticity on mail - DKIM/ARC/SPF/DMARC verification. It also generates the DNS TXT records that must be published so external mail servers can verify your domain's mail.
The section is reached via:
The panel has five sub-sections:
- DKIM
- ARC
- SPF
- DMARC
- DNS records to publish
Changes are only applied after clicking Save button at the bottom of the page
what each protocol does¶
| Protocol | Direction | Purpose |
|---|---|---|
| DKIM (DomainKeys Identified Mail) | Outbound signing / Inbound verification | Cryptographically signs outgoing messages with a private key, tied to a DNS-published public key, so receivers can confirm the message wasn't altered in transit and genuinely originated from this domain |
| ARC (Authenticated Received Chain) | Signing inbound verification for next relay | Preserves the original DKIM/SPF authentication results as a message passes through intermediaries (e.g. mailing lists, forwarding services) that would otherwise break DKIM signatures |
| SPF (Sender Policy Framework) | Inbound verification | Checks that the sending mail server's IP address is authorized to send mail for the sender's domain, based on a DNS record published by that domain |
| DMARC (Domain-based Message Authentication, Reporting & Conformance) | Inbound verification | Ties DKIM and SPF results together and tells receiving servers what to do |
Getting DKIM and DMARC right for your own domain protects your deliverability and brand from spoofing. The verification settings (DKIM, ARC, SPF, DMARC verification dropdowns) instead control how strictly the gateway trusts these signals on inbound mail from other domains.
DKIM¶
| Field | Description |
|---|---|
| Enable DKIM signing | When Active, the gateway signs all outbound mail from this domain with the configured private key. Turn this on before publishing the DKIM DNS record |
| Generate DKIM key (button, top right) | Generates a new RSA-2048 key pair for this domain and populates the Private key (PEM) field |
| DKIM verification | Controls how strictly ан еmail is checked against the sender's published DKIM record |
| Selector | The DKIM selector (e.g. s1) used to publish and look up the public key at <selector>._domainkey.<domain>. Change this only if you need to run multiple keys in parallel (e.g. during a key rotation) — each selector needs its own DNS TXT record |
| Private key (PEM) | The RSA private key used to sign outbound mail. You can either click Generate DKIM key to create one, or paste your own RSA-2048 private key in PEM format |
How to set up DKIM for a new domain¶
- Click Generate DKIM key (or paste an existing RSA-2048 PEM key you manage externally)
- Leave the Selector at the default (
s1) unless you have a reason to change it - Toggle Enable DKIM signing to Active
- Click Save
- Copy the generated
s1._domainkey.<domain>TXT record from the DNS records to publish box (see §7) and add it at your DNS provider - Once the DNS record has propagated, mail signed by this domain will carry a valid DKIM signature
How to rotate a DKIM key¶
- Click Replace key next to Private key (PEM)
- Generate a new key (or paste a new one)
- Publish the new selector's TXT record in DNS before saving/enabling it in production, to avoid a window where signed mail can't be verified
- Save, then remove the old selector's DNS record once you've confirmed the new key is signing correctly
ARC¶
| Field | Description |
|---|---|
| ARC verification (dropdown) | Controls how strictly inbound ARC chains are validated |
| Enable ARC signing | When Active, the gateway adds an ARC seal to forwarded mail, preserving authentication results if the message is later relayed through another system |
| Reuse DKIM key (toggle) | When Active, ARC signing uses the same RSA key configured in the DKIM section above instead of requiring a separate key. Recommended unless you have a specific need to keep the two signatures cryptographically separate |
SPF¶
| Field | Description |
|---|---|
| SPF verification | Controls how strictly inbound mail is checked against the sending domain's published SPF record |
Note: This panel only controls verification of inbound SPF — it does not generate an outbound SPF TXT record for your own domain (no SPF entry appears in §7 "DNS records to publish"). If this domain sends mail through an external relay (e.g. Microsoft 365, as configured in Mail routing → Outbound relay), make sure that provider's SPF
include:mechanism is already published in your domain's own SPF record at your DNS provider, independently of this gateway.
DMARC¶
| Field | Description |
|---|---|
| DMARC verification | Controls how strictly inbound mail is checked against the sender's DMARC policy |
Verification value¶
| UI label | value | Behavior |
|---|---|---|
| Disabled | disable |
Not checked at all. Mechanism doesn't run |
| Optional | relaxed |
Verified and reported in Authentication-Results. Message is always accepted, pass or fail |
| Required | strict |
Verified and reported, and the message is rejected if it hard-fails. Otherwise accepted |
So yes — Disabled = don't check; Optional and Required both check and stamp the result. The only difference between the two is enforcement: Optional never rejects, Required rejects on a hard failure.
What counts as a "hard failure" for Required (per mechanism):
- DKIM — the message carries signatures and all of them fail. No signature at all = none, not a failure → not rejected.
- SPF — a hard -all fail. SoftFail/neutral/none/temp-error are reported but don't reject.
- DMARC — neither DKIM nor SPF aligns and there's an actual fail verdict. No DMARC record published = none → not rejected.
- ARC — the ARC chain fails validation. No chain = none → not rejected.
Two important notes:
- DKIM always runs internally because DMARC needs it. The DKIM setting only controls whether a dkim= result is stamped and whether DKIM failure can reject — it never changes the DMARC verdict.
- Each mechanism is independent, so you can e.g. run DMARC = Required while DKIM/SPF = Optional: bad mail gets rejected on the DMARC verdict, and you still get individual dkim=/spf= lines in the header for visibility.
DNS records to publish¶
This box shows the exact TXT records you must create at your domain's DNS provider so external mail servers can verify mail from this domain. Records shown update automatically based on your DKIM selector and DMARC policy settings above.
| Record | Host | Type | Value |
|---|---|---|---|
| DKIM public key | <selector>._domainkey.<domain> (e.g. s1._domainkey.vrgnservices.eu) |
TXT | v=DKIM1; k=rsa; p=<public key> |
| DMARC policy | _dmarc.<domain> (e.g. _dmarc.vrgnservices.eu) |
TXT | v=DMARC1; p=<policy> (e.g. p=none) |
Use the copy icon in the top-right corner of each record box to copy its exact value. Paste each one as a new TXT record at your DNS registrar/provider, using the Host/Name and Value as shown.
DNS changes can take anywhere from a few minutes to 48 hours to propagate, depending on your provider's TTL settings. DKIM/DMARC enforcement should not be tightened (e.g. enabling signing or moving DMARC policy beyond
none) until you've confirmed the records have propagated and resolve correctly.
Saving changes¶
None of the settings above take effect until you click the orange Save button at the bottom of the page. Save applies all changes across DKIM, ARC, SPF, and DMARC together — there is no per-section save.
Troubleshooting¶
| Symptom | Likely cause |
|---|---|
| Outbound mail fails DKIM at receiving servers | DKIM signing enabled but DNS TXT record not yet published/propagated, or selector mismatch between gateway and DNS. |
| ARC seal missing on forwarded mail | Enable ARC signing is off, or Reuse DKIM key is off with no separate ARC key configured. |
| Can't see the DKIM private key to copy it elsewhere | By design — once saved, the key is masked (<hidden>) and cannot be re-displayed. Use Replace key to issue a new one if you need to move it to a system that doesn't already have a copy. |
| Legitimate mail starts getting quarantined/rejected after a DMARC policy change | Some legitimate sending source isn't DKIM/SPF-aligned yet. Roll the policy back to none, identify the failing source, fix alignment, then re-tighten. |
Update HIN Gateway¶
This document explains:
- How to update a HIN Gateway instance to a newer version.
- How to roll back to a previous version.
Warning
This procedure applies only to versions 0.6.x and later.
How to Update¶
- Go to the
Settingspage and scroll to theSystem versionsection.
-
Click
Other versions. -
From the list of available versions, select the target version.
-
Newer versions use incremental numbering, so the target version will have a higher number than the current one.
-
Click the
Downloadbutton. This only starts the download; it does not install the update yet.
-
Once the download completes, a confirmation dialog appears with two options:
-
Restart now: restarts the machine immediately and installs the new version. Later: postpones the update. The new version is installed the next time the machine restarts.
Major release update
Updating from 0.6.x to 1.1.7 is a major release update. You must take a snapshot backup of the VM before you start the update.
This release includes a required security update, which upgrades Keycloak to 26.8.0. During this upgrade Keycloak changes the database schema and removes a column and indexes that the previous Keycloak version (26.7.4) depends on. Because of this, the Roll back button cannot restore a working 0.6.x version. The VM snapshot is the only way to return to the previous version.
How to Roll Back¶
- Go to the
Settingspage and scroll to theSystem versionsection.
-
Click the
Roll back to vX.X.Xbutton. -
Confirm the action on the confirmation screen. The system will restart and install the previous stable version.
Managing Routed Domains in HIN Gateway¶
HIN Gateway can also handle mail traffic for domains that are not HIN Secure domains. This is a common setup for customers who run their own on-premises mail server infrastructure. This capability is built into HIN Gateway, so customers can adapt their mail setup to their own needs.
In HIN Gateway, these domains are called Routed domains. Mail for a Routed domain passes through HIN Gateway without secure messaging processing: it is simply forwarded to the next relay host.
Add a Routed domain¶
Go to the Domains page, click Add domain, enter the domain name, and click Save.
Configure the Routed domain¶
Activate the domain and configure it the same way as a HIN Secure domain. For detailed steps, see Step 14 - Configure mail transport in the installation guide.
Configure NTP¶
HIN Gateway keeps its clock accurate with NTP (Network Time Protocol). An accurate clock is required: certificate validation, message signing and sign-in sessions all fail if the clock drifts.
How it works:
- By default, HIN Gateway synchronises its time with
ntp.metas.ch - If your network does not allow outbound NTP to the internet, or your organisation runs its own time servers, you can replace the default with one or more custom time servers
- You can add several time servers. If one is not reachable, HIN Gateway uses the next one
Before you start¶
- You need access to the HIN Gateway dashboard
- Get the hostname of the time server(s) from your network administrator
- Make sure HIN Gateway can reach the time server(s) on UDP port 123
Check the current time synchronisation¶
- Log in to the HIN Gateway dashboard.
-
Go to Settings and scroll down to the Time synchronisation section.
The status line shows whether the clock is In sync, together with the IP address of the time server in use, the offset and the stratum. Time servers shows which servers are configured. With no custom servers, it shows Using the default time server (ntp.metas.ch).
Add custom time servers¶
-
In the Time synchronisation section, click Edit. An empty field for the first time server appears.
-
Enter the hostname of the time server (for example
ntp.example.com). - Use the switch next to the field to choose the type of entry:
- Server: a single NTP server
- Pool: an NTP pool, a hostname that resolves to several servers (for example
ch.pool.ntp.org). HIN Gateway uses several servers from the pool
-
To add another time server, click Add server and repeat steps 2 and 3. You can mix pools and servers.
To remove an entry, click the minus icon next to it.
-
Click Save.
HIN Gateway now synchronises its time with the configured time servers instead of the default one.
Verify the configuration¶
Check the status line in the Time synchronisation section. It should show In sync and the IP address of one of your time servers. To force an immediate synchronisation, click Sync now.
Return to the default time server¶
Click Edit, remove all entries with the minus icon and click Save. HIN Gateway then uses ntp.metas.ch again.
Inbound connection authentication¶
Inbound connection authentication allow an external system, such as a mail relay, an application or a mail client, send email for one of your domains through HIN Gateway by logging in with a username and password. This is useful when the sender cannot be allowed only by its IP address.
How it works:
- The sender connects to HIN Gateway on the submission port 587 and authenticates with SMTP AUTH
- Credentials are created per domain in the HIN Gateway dashboard
Before you start¶
- You need access to the HIN Gateway dashboard
- The domain you want to send from must already be added in HIN Gateway and onboarded
- TLS must be enabled for the domain. Passwords are only accepted over TLS, so credentials do not work without a TLS certificate
- Have access to the configuration of the sending system (MTA / application), since you will enter the new credentials there
Create a credential¶
- Log in to the HIN Gateway dashboard.
- Go to Domains and select the domain you want to send from
-
Scroll down to the SMTP authentication (port 587) section.
Any credentials that already exist for the domain are listed here.
TLS must be enabled
If the domain has no TLS certificate yet, the section shows the message No TLS certificate yet. Credentials do not work until TLS is enabled, because passwords are only accepted over TLS.
To enable TLS, go to the Configure TLS section of the same domain page and add a certificate, or click Generate TLS certificate to create one. Then return to this section.
The Verify a trusted network switch controls where credentials can be used from. When it is Off, credentials work from any IP address. Turn it on if you want to restrict logins to
Trusted networksonly. -
Click Add credential. A form with two fields opens:
Choose one of the following:
- Generate the credentials automatically (recommended): leave both Username and Password empty. HIN Gateway generates a username and a strong random password
- Set your own credentials: enter Username and Password. The password must be at least 16 characters long.
-
Click Create
-
Copy the username and password
Warning
The password is shown only once. Click Copy and store it in a safe place (for example a password manager) before you continue. If the password is lost, use Rotate password to issue a new one.
-
Click Done. The new credential now appears in the list.
Configure the sending system¶
Enter the credential in the sending MTA, application:
| Setting | Value |
|---|---|
| SMTP server | The IP of your HIN Gateway |
| Port | 587 |
| Encryption | STARTTLS |
| Authentication | Username and password (SMTP AUTH) |
| Username | The username from step 6 |
| Password | The password from step 6 |
The sender address (MAIL FROM) must belong to the domain the credential was created for, otherwise the message is rejected.
Manage existing credentials¶
Each credential in the SMTP authentication (port 587) section has two actions:
- Rotate password: generates a new password for the credential. The old password stops working, so update the sending system right away.
- Delete: removes the credential. Any system that still uses it can no longer send through HIN Gateway.
!!! Tip Create a separate credential for each sending system. You can then rotate or revoke access for one system without affecting the others.
Outbound connection authentication¶
When HIN Gateway forwards messages for a domain to the next hop (for example a mail relay or a mail server), that next hop may require the sender to log in before it accepts mail. Outbound connection authentication lets HIN Gateway authenticate to the next hop with a username and password (SMTP AUTH).
How it works:
- Each domain has its own Outbound relay (host and port) in the HIN Gateway dashboard
- When Require authentication is active, HIN Gateway logs in to the outbound relay with the credentials you enter before it forwards a message
Before you start¶
- You need access to the HIN Gateway dashboard
- The domain must already be added in HIN Gateway and onboarded
- Get the username and password from the administrator of the receiving side (the relay or mail server that requires authentication)
Configure the credentials¶
- Log in to the HIN Gateway dashboard.
- Go to Domains and select the domain.
-
Scroll down to Mail routing and find the Outbound section.
Check that Outbound relay shows the correct host and port of the next hop (for example
relay.example.com:25). These credentials are used for this relay only. -
Click the Require authentication switch so that it shows Active. The Username and Password fields appear.
-
Enter the Username and Password configured on the receiving side. Click the eye icon to show the password and check it for typos.
- Scroll down and click Save.
From now on, HIN Gateway authenticates to the outbound relay every time it forwards a message for this domain.
Verify the configuration¶
Send a test message through HIN Gateway to a recipient behind the outbound relay and check that it arrives. If it does not, open Logs in the dashboard and look for authentication errors from the relay. The most common causes are:
- Wrong username or password
- The relay does not support or allow SMTP AUTH on the configured port
- The Outbound relay host or port is wrong
Change or remove the credentials¶
- Change the password: when the receiving side changes the password, enter the new one in the Password field and click Save. Until then, messages to the relay fail authentication.
- Turn authentication off: click Require authentication so that it shows Inactive and click Save. HIN Gateway then connects to the relay without logging in.
Server install
Container
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:
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:
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, 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.
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):
WG_PRIVATE_KEY— Generated by IRISAgent on first run, saved tocustomer-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:
Manually copy files to the server
SSH to server
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
Step 3: What Install Does¶
The install script (install.sh) performs the following steps:
- Check dependencies — Detects Docker, Docker Compose, and
jq. If missing, installs them automatically (supports Ubuntu/Debian, RHEL/AlmaLinux/Rocky). - Load and validate
customer-config.sh— Checks required fields (SERVER_STATIC_IP,CUSTOMER_NAME,DEPLOYMENT_NAME). Auto-derives optional fields (MXEngine URL, etc.). - Generate
.envfrom customer config — Auto-generates passwords if not set. - Start all services via Docker Compose (infrastructure + applications).
- Initialize Vault — The
vault-initcontainer initializes, unseals, and creates KV-v2 secret mounts. Optionally writes the WireGuard private key to Vault. - Save Vault keys to
secrets/vault-keys.jsonand update.envwith the root token. The token is also saved tocustomer-config.shfor persistence across VM recreations. - Restart application services to pick up the Vault token.
- Save WireGuard private key to
customer-config.sh— extracted from Vault after IRISAgent generates it. - 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:
- WireGuard public key — extract from irisagent logs:
DEPLOYMENT_NAME— from yourcustomer-config.shSERVER_STATIC_IP— the public IP of your Stargate serverWG_INTERFACE_PORT— only if you changed it from the default19818
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
Check for successful WireGuard handshake
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=noneat_dmarc.<YOUR_DOMAIN> - PTR: set reverse DNS for the Stargate IP to match
MAIL_HOSTNAME
Step 6.2 Relay outbound mail back through your mail platform (recommended for M365 / Exchange Online)¶
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:
- 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).
- Outbound connector "Send to MX" - delivers to the recipient's MX, activated only by transport rule.
- Transport rule
set_header- tags outbound mail with a header likeoutgoing: outgoing_<domain>before it leaves O365 the first time, so the return trip can recognize it. - Transport rule
outgoing_to_mx- matches theoutgoing_<domain>header on mail coming back from the Stargate and routes it via the "Send to MX" connector. - 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:
This runs start.sh, which:
- Starts infrastructure services
- Unseals Vault using stored keys
- Starts application services
(./scripts/start.sh still works directly if you prefer.)
Stop Services¶
(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:
Or using the scripts directly:
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.
Scripts Reference¶
| Script | Purpose |
|---|---|
install.sh |
First-time installation (Docker, Vault). Domain/cert/peer setup happens in the dashboard afterwards. |
update.sh |
Regenerate .env from customer-config.sh (adding new template variables), pull the pinned images and recreate containers (preserves Vault token). On VM appliances, versions are changed from the dashboard instead - see Update HIN Gateway |
start.sh |
Start services and unseal Vault |
stop.sh |
Stop containers (data preserved) |
backup.sh |
Full backup (database, Vault keys, config, certificates); --config-only saves only customer-config.sh |
restore.sh |
Restore from backup archive (works on fresh machine); --config-only restores only customer-config.sh |
purge.sh |
|
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.
Stargate Docker advanced configuration¶
Data Layout¶
All writable state - generated config, secrets, backups, logs, and every service's own data - lives under /var/data, not inside the docker-compose/ checkout:
| Path | Contents |
|---|---|
/var/data/vereign/.env |
Generated environment file (do not edit by hand) |
/var/data/vereign/customer-config.sh |
Your install-time settings (copied from customer-config-prod.example.sh) |
/var/data/vereign/secrets/ |
Vault unseal keys, WireGuard private key, S/MIME CSR |
/var/data/vereign/tls/ |
Generated TLS certificate/key served by Caddy |
/var/data/vereign/keycloak/ |
Generated Keycloak realm/config |
/var/data/vereign/apisix/ |
Generated APISIX config |
/var/data/vereign/update.log |
Output of every update.sh run, newest at the end - plain Compose updates, dashboard updates on older pre-bootc VMs, and runs after changing customer-config.sh (dashboard updates on the VM appliance do not use it; see An update failed) |
/var/data/backups/ |
Backup archives (.tar.gz) |
/var/data/<service>/ |
Per-service data volumes: postgres, vault, seaweedfs, stalwart, clamav, loki, alloy, textfile_collector, dashboard_cache |
The docker-compose/ tree (scripts, docker-compose.yml, config templates) is read-only at runtime - a bootc image layer on VM appliances - and nothing under it is ever written to. docker-compose/scripts/init-data-layout.sh creates the /var/data tree and sets per-service ownership; it runs automatically during install.sh and is safe to re-run.
Testing with a different root
Set the STARGATE_DATA_DIR environment variable to point at a different root instead of /var/data - useful for local testing without touching the real path. Every script and docker-compose.yml itself resolve their data paths from it.
Prefer the wrapper scripts for anything that (re)creates containers
Because .env no longer lives next to docker-compose.yml, a bare docker compose up -d run from inside docker-compose/ falls back to default credentials instead of your real ones. Use ./scripts/start.sh, ./scripts/update.sh, ./scripts/stop.sh, etc. - they already pass --env-file /var/data/vereign/.env - or add --env-file /var/data/vereign/.env yourself to a manual docker compose command. Read-only commands (docker compose ps, logs, exec) are unaffected either way.
Backups¶
Automatic Backups¶
- Daily backups run at 2:00 AM via cron (set up during install)
- Backups stored in
/var/data/backups/as timestamped.tar.gzfiles - Old backups (>7 days) are automatically cleaned up
What's Included in Backups¶
- Full PostgreSQL dump (all databases with users and permissions)
- Individual database dumps (for partial restore if needed)
- Vault keys (
vault-keys.jsonfor unsealing) - Customer configuration (
customer-config.shwith WireGuard key) - S/MIME CSR and certificates (any
.crt,.pem,.cerfiles) - Backup manifest (
manifest.jsonwith metadata)
Manual Backup¶
Creates a compressed archive in /var/data/backups/YYYYMMDD_HHMMSS.tar.gz.
Restore from Backup¶
To restore on a new machine or after a purge. Copy the backup archive to the new machine and execute:
The restore script will:
- Stop any running services
- Extract and validate the backup
- Install Docker if needed
- Restore customer configuration
- Start infrastructure services (PostgreSQL, Vault, MinIO)
- Restore the database
- Unseal Vault with backed-up keys
- Start application services
Configuration-only backup and restore¶
To carry only customer-config.sh across a purge and reinstall, for example to change the server IP before onboarding (see Troubleshooting):
./scripts/backup.sh --config-only
./scripts/restore.sh --config-only /var/data/backups/20260130_143022_config-only.tar.gz
backup.sh --config-onlysavescustomer-config.shand any custom NTP servers (ntp.sources) to/var/data/backups/<timestamp>_config-only.tar.gz. It needs no running services, leaves out.env, the Vault keys and the databases, and deletes no older backups.restore.sh --config-onlyrestores only those two files, from a configuration-only archive or from a full backup. It asks for no confirmation, keeps an existing file ascustomer-config.sh.bak.<timestamp>, and prints the next steps.- A full restore of a configuration-only archive is refused.
The archive contains the generated passwords and the WireGuard private key, so treat it as a secret.
Partial Restore (single database)¶
If you only need to restore one database:
Extract backup¶
Restore a specific database¶
cat /tmp/20260130_143022/database/mxengine.sql | docker exec -i stargate-postgres psql -U postgres -d mxengine
Updating Stargate¶
How you update depends on how Stargate is installed:
- VM appliance (bootc image) - the HIN Gateway VM images. The whole OS, including Docker, the deployment scripts,
docker-compose.ymland the pinned service image versions, ships as one versioned image. You update by switching to a newer image and rebooting. - Plain Compose install - a
git cloneof this repository on a machine you manage yourself. You update by checking out a newer release tag.
VM appliance (bootc)¶
On the appliance, /usr/share/stargate-deployment is part of the read-only OS image. There is no git checkout to pull or reset, so do not run git or update.sh --release there. All state - configuration, secrets, mail and databases - lives on the Data Disk under /var/data and is not touched by an update.
Update from the dashboard¶
Use the System version section of the dashboard's Settings page - see Update HIN Gateway for the step-by-step guide. When you start the download, the ops-agent:
- Takes a full backup with
backup.sh, labelled<current version>-pre_update_to_<target version>, in/var/data/backups/. If the backup fails, the update is cancelled and nothing is changed. The one exception is a backup whose Vault part is incomplete: the update continues and the ops-agent logs the warningpre-update backup is missing Vault secrets; continuing. - Downloads and stages the target OS image (
bootc switch). The running system is not changed yet. - Activates the new version on the next reboot - immediately with
Restart now, or the next time the machine restarts withLater.
Update from the command line¶
Log in to the VM as hinadmin (ssh hinadmin@<VM IP address>) and use the shortcuts that ship with the image:
hingateway-tags # list the published versions
hingateway-switch vX.Y.Z # download and stage the target version
sudo systemctl reboot # boot into it
hingateway-status # confirm: Booted: vX.Y.Z, Rollback: <previous version>
Note
Unlike the dashboard, hingateway-switch does not take a backup first. Run sudo /usr/share/stargate-deployment/docker-compose/scripts/backup.sh --label <current version>-pre_update_to_vX.Y.Z before switching, so the archive is named like the one the dashboard creates. The current version is the one hingateway-status shows as booted.
Rollback¶
The appliance keeps two OS deployments on disk: the running one and the previous one. To go back to the previous one, use the Roll back button on the dashboard (see Update HIN Gateway) or run:
The rollback needs no download. To go back further than the previous version, switch to that version explicitly with hingateway-switch <tag>.
Rollback restores the OS image, not the data
A rollback swaps only the OS image; the data under /var/data stays as the newer version left it. If the newer version changed a database schema (for example the Keycloak upgrade in 1.1.7), the older version cannot run against it. In that case roll back the OS image and then restore the backup taken before the update - the archive in /var/data/backups/ whose name ends in pre_update_to_<target version>.tar.gz - with sudo /usr/share/stargate-deployment/docker-compose/scripts/restore.sh <archive>. The nightly backup deletes archives older than 7 days, this one included, so copy it off the VM if you may need it later. The update from 0.6.x to 1.1.7 is the exception: there the VM snapshot is the only way back (see Update HIN Gateway).
Do not roll back across the first Data Disk image
The first upgrade onto an image with Data Disk support is one-way. An older image without Data Disk support does not mount the Data Disk: the stack comes up against an empty /var/data while your real data stays untouched on the unmounted disk. This looks like total data loss but is not - upgrading forward again remounts the disk.
What an update keeps¶
/etc is merged with the new image (local edits win) and /var - including /home, Docker data and user accounts - is kept. Packages installed ad hoc with rpm-ostree install survive reboots but are removed by the next switch; only what is part of the image is permanent.
Plain Compose install¶
This section applies only to a git clone of this repository that you manage yourself, not to the VM appliance. Run all commands from the docker-compose/ directory of your checkout. The scripts read and write files under /var/data/vereign/ that only root can read, so run them as root or with sudo. The checkout should belong to root as well (clone it as root): when the scripts run as root in a checkout owned by another user, git refuses to read it and the deployment's version is shown as unknown. If the checkout belongs to another user, allow it once with sudo git config --global --add safe.directory <path to the checkout>, and run the git commands below as that user, without sudo.
1. Create a backup before updating¶
Copy the archive off the machine if you may need it later: the nightly backup deletes archives older than 7 days.
2. Check out the target release¶
The repository is the single source of truth for tracked files, so update by checking out a release tag. This replaces tracked files (scripts, docker-compose.yml with the pinned image versions, config templates) with that release's versions:
sudo git fetch --tags origin
sudo git tag --sort=-creatordate | head # most recent releases
sudo git checkout -f vX.Y.Z
3. Check for new configuration variables¶
Compare the variable names in the release's config template with your existing config to see if new variables were added. Only names are compared, so your values and credentials are not printed; lines starting with > are new variables:
sudo -v # enter the sudo password before the comparison starts
diff <(sudo grep -oE '^[A-Za-z_][A-Za-z0-9_]*=' /var/data/vereign/customer-config.sh | sort) \
<(grep -oE '^[A-Za-z_][A-Za-z0-9_]*=' customer-config-prod.example.sh | sort)
Do this before the next step: update.sh adds every missing variable to your customer-config.sh with the template's default value, so afterwards the new variables no longer show up as differences. Set any new variable that needs a non-default value in customer-config.sh now.
4. Apply the release¶
update.sh regenerates .env from customer-config.sh, pulls the images pinned in docker-compose.yml and recreates the affected services. Its output is appended to /var/data/vereign/update.log, newest run at the end.
5. Restart the stack¶
update.sh force-recreates Vault (and Dozzle, if enabled) but recreates the other services only when their definition changed, so a service whose configuration file changed in the release (for example the Caddy, APISIX or Loki config) can keep running with the old file. start.sh recreates every container, so all of them pick up the new files. This restarts the stack a second time, so plan the update for a maintenance window.
Note
Your customer-config.sh, .env, and secrets/ directory live under /var/data/vereign/, outside this repository checkout entirely, so a git checkout -f never touches them - your configuration and credentials are preserved. Always put customization in customer-config.sh, never by editing tracked files such as docker-compose.yml: the next checkout reverts any tracked-file edits. This is intentional; keeping every deployment identical to the repository is what lets updates apply reliably and without manual conflict resolution.
To roll back, check out the previous release tag and run steps 4 and 5 again. If the newer release changed a database schema, do not run steps 4 and 5: after checking out the previous tag, restore the pre-update backup with sudo ./scripts/restore.sh <archive> instead (see Restore from Backup). restore.sh stops the stack, restores the data and starts the services of the checked-out release.
update.sh already removes unused images older than 24 hours. To free disk space right away, run sudo docker image prune -af.
Do not change versions manually¶
Warning
Image versions live in docker-compose.yml, not in customer-config.sh or .env - those no longer hold *_VERSION values. Do not hand-edit an image tag: versions are released and tested together as a set, and hand-picking one produces an untested combination. On the VM appliance the file is read-only anyway. Always move between releases as described above.
Configuration¶
The .env file (/var/data/vereign/.env) is generated by install.sh from customer-config.sh (/var/data/vereign/customer-config.sh). Domain, certificate, and WireGuard settings are managed at runtime by the dashboard (/installation, /onboarding, /mail) — they are not held in .env. To customize the install-time settings, edit customer-config.sh and re-run install.sh.
Key sections in the generated .env:
## PostgreSQL (auto-generated if empty in customer-config.sh)
POSTGRES_USER=postgres
POSTGRES_PASSWORD=<auto-generated>
## Vault (auto-populated after initialization)
VAULT_TOKEN_<SERVICE>=<auto-generated, one per service>
## S3 Object Storage (SeaweedFS)
S3_ACCESS_KEY=minioadmin
S3_SECRET_KEY=<auto-generated>
## Mail Outbound Path
MXENGINE_PUBLIC_ADDRESS=http://localhost:8084
OUTBOUND_SEALER_MX_DOMAIN=hintest.ch
## WireGuard
WG_LOCAL_IP=203.0.113.50
WG_INTERFACE_PORT=19818
WG_TRANSPORT_MODE=tcp
Warning
Do not edit .env directly. Changes will be overwritten on re-run of install.sh. For runtime configuration (domains, hostname, peers, S/MIME), use the dashboard.
Service URLs¶
| Service | URL/Port |
|---|---|
| Dashboard | https://localhost |
| smimekeys-client | http://localhost:8081 |
| policy | http://localhost:8082 |
| irisagent | http://localhost:8083 |
| mxengine HTTP | http://localhost:8084 |
| Stalwart SMTP | localhost:25 |
| APISIX Gateway | http://localhost:9080 |
| Keycloak | https://localhost:8180 |
Health Checks¶
All services expose a /liveness endpoint:
curl http://localhost:8081/liveness # smimekeys-client
curl http://localhost:8082/liveness # policy
curl http://localhost:8083/liveness # irisagent
curl http://localhost:8084/liveness # mxengine
Monitoring¶
Prometheus Metrics¶
All application services expose Prometheus metrics on port 2112 (internally), mapped to different host ports:
| Service | Metrics Port | Metrics URL |
|---|---|---|
| smimekeys-client | 2113 |
http://localhost:2113/metrics |
| irisagent | 2114 |
http://localhost:2114/metrics |
| policy | 2115 |
http://localhost:2115/metrics |
| mxengine | 2116 |
http://localhost:2116/metrics |
| node-exporter | 9100 |
http://localhost:9100/metrics |
Prometheus Scrape Config Example¶
scrape_configs:
- job_name: 'stargate-smimekeys'
static_configs:
- targets: ['<host>:2113']
- job_name: 'stargate-irisagent'
static_configs:
- targets: ['<host>:2114']
- job_name: 'stargate-policy'
static_configs:
- targets: ['<host>:2115']
- job_name: 'stargate-mxengine'
static_configs:
- targets: ['<host>:2116']
- job_name: 'stargate-node'
static_configs:
- targets: ['<host>:9100']
Quick Metrics Check¶
## Check all metrics endpoints
curl -s http://localhost:2113/metrics | head -20 # smimekeys-client
curl -s http://localhost:2114/metrics | head -20 # irisagent
curl -s http://localhost:2115/metrics | head -20 # policy
curl -s http://localhost:2116/metrics | head -20 # mxengine
curl -s http://localhost:9100/metrics | head -20 # node-exporter
Log Collection (Alloy → Loki)¶
Alloy collects logs from application containers and ships them to Loki.
Containers monitored:
- stargate-apisix
- stargate-keycloak
- stargate-dashboard
- stargate-smimekeys-client
- stargate-policy
- stargate-policy-sync
- stargate-irisagent
- stargate-mxengine
Configuration in .env:
## Loki push URL
LOKI_URL=https://loki.example.com
## Hostname label for logs (auto-set to DEPLOYMENT_NAME)
ALLOY_HOSTNAME=stargate-acme
Labels added to logs:
environment=<DEPLOYMENT_NAME>- Identifies the deploymenthost=<ALLOY_HOSTNAME>- Identifies the host (same as deployment name)container=<container-name>- Container nameservice=<service-name>- Service name (e.g., smimekeys-client, policy)level=<log-level>- Extracted from JSON logs if available
Query logs in Grafana:
{environment="stargate-acme"} |= "error"
{environment="stargate-acme", service="mxengine"}
{environment="stargate-acme", level="error"}
Verify Alloy is working:
Note: The VM's public IP must be whitelisted in Loki's ingress configuration.
Stalwart MTA + mtaconf¶
Stargate uses Stalwart as the mail transfer agent and mtaconf as the configuration daemon. The dashboard sends domain and relay configuration to mtaconf's REST API, which pushes it to Stalwart via the management CLI.
Mail Flow Architecture¶
External Mail Server
│
▼ (port 25)
┌─────────────────────────────────────────────────────┐
│ stalwart (stargate-stalwart) │
│ │
│ Port 25 (smtp listener) │
│ │ │
│ ▼ │
│ content_filter → smtp:[mxengine]:1587 │
│ │ │
└────┼────────────────────────────────────────────────┘
│
▼ (port 1587)
┌─────────────────────────────────────────────────────┐
│ MXEngine (stargate-mxengine) │
│ │
│ Port 1587 (SMTP input) │
│ │ │
│ ▼ │
│ Sign/encrypt/process mail │
│ │ │
│ ▼ │
│ Deliver back to stalwart for relay │
│ │ │
└────┼────────────────────────────────────────────────┘
│
▼ (port 10026)
┌─────────────────────────────────────────────────────┐
│ stalwart (stargate-stalwart) │
│ │
│ Port 10026 (reinject listener) │
│ │ │
│ ▼ │
│ transport → relay to destination MX │
│ │ │
└────┼────────────────────────────────────────────────┘
│
▼ (port 25)
Destination Mail Server (via MX lookup)
Antivirus scanning: Inbound mail is scanned by ClamAV (stargate-clamav), wired into Stalwart as a milter at the SMTP DATA stage on both the public (:25) and reinject (:10026) listeners. Infected mail is rejected at the SMTP level; if ClamAV is unreachable the message is deferred rather than delivered unscanned (fail-closed). ClamAV's signature database lives in the clamav_data volume and is kept current by freshclam in the background.
Mail Relay Configuration¶
All mail configuration that varies per deployment (mail domains, hostname, relay host, per-domain relay maps, allowed networks) is set through the dashboard's /mail page at runtime. The dashboard POSTs the configuration to mtaconf's REST API, which applies it to Stalwart without restarting the container.
There is no per-domain config in customer-config.sh or .env - operators add or change domains through the UI.
Mail Routing (Migrating from Old MGW)¶
Key difference from the old HIN-MGW
In the old MGW, you had to manually configure a target server per domain. In Stargate, mail routing is decided by DNS MX records by default - Stalwart resolves each domain's MX at delivery time. The dashboard's /mail page lets you override this per-domain (e.g. to relay back through your M365 / Exchange tenant) without touching DNS.
Default - automatic via DNS MX:
For each of your domains, make sure there is an MX record in DNS pointing to the corresponding Exchange (or other mail) server:
domain1.com MX 10 exchange1.domain1.com
domain2.com MX 10 exchange2.domain2.com
domain3.com MX 10 exchange3.domain3.com
This works for any number of domains - each domain can point to a different mail server, and Stalwart will route accordingly.
If Stargate is the only MX record for a domain, Stalwart will filter it out and have no delivery target. Add a second MX record pointing to your mail server with a higher priority (= lower number) so Stalwart uses it as the delivery target:
example.com MX 10 exchange.example.com ← delivery target (mail server)
example.com MX 20 stargate.example.com ← inbound gateway (Stargate)
Alternative - explicit per-domain relay (sender-based):
For relay-back through M365 / Exchange Online, configure per-domain relay targets through the dashboard's /mail page. Mail from senders not in the map falls back to MX lookup.
Ports¶
| Port | Purpose |
|---|---|
25 |
Main SMTP listener (external connections) |
10026 |
Reinjection port (mxengine → stalwart, internal only) |
1587 |
MXEngine SMTP input (stalwart → mxengine, internal only) |
8080 |
Stalwart management API + mtaconf REST API (internal only) |
Using Exchange?
See Exchange-integration for the full Exchange Online / On-Premises connector and transport rule setup.
Verification¶
Check Stalwart status
Check logs
Test connection to port 25
Test internal port 10026 (from mxengine container)
Updating the mtaconf image¶
Like every service, the mtaconf image version is part of a release and changes only together with it - not by editing its tag by hand. To get a newer mtaconf, update to a newer release as described in Updating Stargate.
Stargate Troubleshooting¶
Mail not being processed by mxengine:
- Check content_filter is configured: verify mtaconf logs show successful push
- Verify mxengine is reachable:
docker exec stargate-stalwart nc -zv mxengine 1587
Mail stuck after mxengine processing:
- Check mxengine outbound config: OUTBOUND_SMTP_HOST=stalwart, OUTBOUND_SMTP_PORT=10026
- Verify port 10026 listener is active in Stalwart
- Check allowed relay networks include Docker network (172.x.x.x/16)
Greylisting errors (450 4.7.1):
- This is normal! The destination server is temporarily rejecting mail
- Stalwart automatically retries after a configurable delay
- Check queue via management API
Microsoft blocking IP (S3140):
- Your server's IP has poor reputation with Microsoft
- Request delisting at: https://sender.office.com
- May take 24-48 hours to take effect
DNS Lookup Failures:
- Use the dashboard's
/mailpage to set an explicit relay host or per-domain relay map (skips MX-based discovery)
Connection Refused on port 25:
- Ensure port 25 is not blocked by the firewall
- Check if another service is using port 25:
ss -tlnp | grep :25
WireGuard (Agent-to-Agent Communication)¶
IRISAgent uses WireGuard to establish secure encrypted tunnels between Stargate instances for delivering sealed messages.
How It Works¶
Each Stargate instance uses its server's real static public IP as the WireGuard tunnel address. This guarantees uniqueness across all deployments without manual coordination.
block
columns 5
block:Stargate["Your Stargate (203.0.113.50)"]:2
columns 1
A
space
A --> B
A["IRISAgent (203.0.113.50:19818)"]
B["Sealed message delivery via WG tunnel"]
end
blockArrowId1<["WG Tunnel (TCP)"]>(x):1
block:mxengine["HIN Test (5.102.144.182)"]:2
columns 1
C
space
C --> D
C["IRISAgent (5.102.144.182:19818)"]
D["Receive sealed message"]
end
WireGuard Configuration¶
WireGuard settings in customer-config.sh:
## ==============================================================================
## Server IP — used as WireGuard tunnel address and MXEngine callback URL
## ==============================================================================
SERVER_STATIC_IP="203.0.113.50" # Your server's real static public IP
## ==============================================================================
## WireGuard local settings (typically left at defaults)
## ==============================================================================
WG_PRIVATE_KEY="" # Auto-generated by IRISAgent, then saved back to config
WG_INTERFACE_PORT="19818" # Default WireGuard port
WG_TRANSPORT_MODE="tcp" # "tcp" (default) or "udp"
Info
WG_LOCAL_IP is auto-derived from SERVER_STATIC_IP. You do not need to set it separately.
Peer Connection Setup¶
WireGuard peer details (public key, endpoint, allowed IPs, etc.) are configured at runtime through the dashboard's /installation page. There is no WG_PEER_* block in customer-config.sh anymore — the peer is set up after the stack is up.
For first-time setup with the HIN Test environment:
- Bring up the stack with
./scripts/install.sh. - Open the dashboard, follow
/installationto start the nonce / HIN handshake. - Open the IRISAgent logs (
docker compose logs irisagent) and copy thewireguard public key:line. Send it together withDEPLOYMENT_NAMEandSERVER_STATIC_IPto Vereign (kalin.canov@vereign.com) so they can register your peer on the CA side. - After Vereign confirms registration, complete
/onboardingin the dashboard to issue the S/MIME certificate.
For any additional peer (peer-to-peer between two Stargates), exchange public keys + endpoints with the other party and add the connection through the IRISAgent API:
curl --location 'localhost:8083/v1/connections' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
"allowedIps": "<IP of new peer>/32",
"description": "<short description>",
"endpoint": "<IP of new peer>:19818",
"externalId": [
"<domain of new peer>"
],
"name": "<Name of new peer>",
"presharedKey": "",
"publicKey": "<public key of new peer>",
"status": "completed",
"transport": "tcp",
"wireguardIp": "<IP of new peer>",
"wireguardPort": 10080
}'
WireGuard Verification¶
Check IRISAgent WireGuard interface
Check connection in database
docker exec stargate-postgres psql -U postgres -d irisagent \
-c "SELECT connection_id, name, endpoint, wireguard_ip, transport, status FROM connections;"
Check connection external IDs (used for routing)
docker exec stargate-postgres psql -U postgres -d irisagent \
-c "SELECT connection_id, external_id FROM connection_external_ids;"
Test WireGuard connectivity (check tunnel status from host)
Check IRISAgent logs for tunnel activity
WireGuard Troubleshooting¶
No WireGuard interface:
- Check IRISAgent logs:
docker logs stargate-irisagent - Verify
WG_LOCAL_IPis set in.env(auto-derived fromSERVER_STATIC_IP— should be this server's static public IP)
Peer not reachable:
- Verify remote endpoint is accessible:
nc -zv <endpoint_host> <endpoint_port> - Check firewall allows TCP+UDP port 19818
- Verify public keys match on both ends
- If TCP has issues, try setting
WG_TRANSPORT_MODE="udp"in customer-config.sh
Connection not in database:
- Re-run the dashboard's
/installationpage to re-establish the peer connection - Check irisagent logs:
docker logs stargate-irisagent
Policy Sync¶
The policy-sync service automatically syncs OPA/Rego policies from a Git repository to the PostgreSQL database.
How Policy Sync Works¶
block
columns 8
A:2 space B:2 space C:2
A["Git Repository
policies/
alpha/
outbound/
..."]
A-->B
B["policy-sync:
- Clone/Pull repo
- Parse .rego files
- Upsert to database
- Runs every 1h"]
B-->C
C["PostgreSQL
- policy database
- policies table"]
Policy Sync Configuration¶
Settings in customer-config.sh:
## Git repository containing policies (pre-configured with HIN Stargate policies)
POLICY_SYNC_REPO_URL="https://github.com/Health-Info-Net-AG/Stargate-policies.git"
## Optional: Authentication for private repos
POLICY_SYNC_REPO_USER=""
POLICY_SYNC_REPO_PASS=""
## Optional: Specific branch (default: main)
POLICY_SYNC_REPO_BRANCH=""
## Optional: Subfolder within repo containing policies
POLICY_SYNC_REPO_FOLDER=""
## Sync interval (default: 1h)
POLICY_SYNC_INTERVAL="1h"
Policy Sync Verification¶
Manual Trigger¶
To force an immediate sync:
Vault¶
Vault Mounts¶
Vault's API/UI port (8200) is not published to the host; access Vault via the CLI inside the container (see Manual Vault Operations below).
The following KV-v2 secret engines are created:
secret-smimekeys-clientsecret-policysecret-irisagentsecret-mxenginesecret-mtaconf
Manual Vault Operations¶
Each service has its own token in /var/data/vereign/secrets/service-tokens.env, limited to that service's mount. Administrative commands such as listing mounts need the root token from vault-keys.json in the same directory. The examples read the token from the file, so it does not appear on the command line or in the shell history.
Databases¶
PostgreSQL databases created:
smimekeys_clientpolicyirisagentmxengine
Connect to PostgreSQL¶
Or connect externally
Policies (Rego)¶
MXEngine uses OPA/Rego policies stored in PostgreSQL to determine mail delivery strategy.
Recommended: Use policy-sync to automatically sync policies from a Git repository. See Policy Sync section.
View Current Policy¶
Policy Location¶
- MXEngine config:
POLICY_OUTBOUND: "outbound/delivery" - Database:
policydatabase,policiestable - Managed by:
policy-syncservice (syncs from Git repository)
Logs¶
Troubleshooting¶
Certificate issuance failed / WireGuard tunnel not established¶
This is the most common issue after initial installation. The S/MIME certificate cannot be issued because the WireGuard tunnel to the HIN CA is not established.
Symptoms:
- The dashboard's
/onboardingpage reports CSR submission failure - smimekeys-client logs show:
issue certificate error: certcatunnel: error sending request: irisagent: ... context deadline exceeded
Root causes (check in order):
- Peer not registered on HIN CA - Your WireGuard public key must be registered on the HIN side. Provide HIN with:
Along with your DEPLOYMENT_NAME, SERVER_STATIC_IP, and WG_INTERFACE_PORT (if changed from 19818).
-
Firewall blocking port 19818 - Ensure
19818/TCPis open both inbound and outbound on the Stargate server. -
Wrong hostname - If the Stalwart hostname is still set to the template default (
mail.example.com), update it via the dashboard's/mailpage.
After the issue is resolved:
Re-open the dashboard's /onboarding page to regenerate the CSR and resubmit it through the now-up tunnel.
See Step 5: WireGuard Peer Registration for the full process.
Vault is sealed after restart¶
Run the start script which handles unsealing:
Cannot pull images¶
Login to the registry:
Service won't start¶
Check logs:
Reset everything¶
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.
Files Structure¶
The deployment is split across two trees: the read-only repository checkout (docker-compose/) and the writable data root (/var/data, overridable via STARGATE_DATA_DIR - see Data Layout above). Nothing is ever written under docker-compose/.
docker-compose/ (read-only)¶
docker-compose/
├── config
│ ├── apisix
│ │ ├── apisix.yaml.template
│ │ └── config.yaml
│ ├── keycloak
│ │ └── realm-stargate.json
│ ├── nats
│ │ └── nats.conf
│ ├── nginx
│ │ ├── dashboard.conf
│ │ └── keycloak.conf
│ ├── alloy
│ │ └── config.alloy # Alloy log shipping config
│ └── vault
│ └── vault.hcl # Vault configuration
├── customer-config-prod.example.sh # Config template (copied to /var/data/vereign/customer-config.sh)
├── docker-compose.yml # Main compose file
├── init
│ └── postgres
│ └── 01-create-databases.sql
└── scripts
├── backup.sh # Full backup (DB, Vault, config, certs) -> /var/data/backups
├── gather-app-versions.sh # Collects app versions for node-exporter metrics
├── health-check.sh # Comprehensive health check of all services
├── init-data-layout.sh # Creates the /var/data tree and sets per-service ownership
├── init-keycloak.sh
├── init-vault.sh # Vault initialization (used by vault-init container)
├── install.sh # First-time installation (Docker, Vault). Domain/cert/peer setup happens in the dashboard afterwards.
├── purge.sh # Delete all data (destructive!)
├── restore.sh # Restore from backup archive
├── send-logs-to-support.sh # Paste logs online and get a link that you will provide to support
├── start.sh # Start services + unseal Vault
├── stop.sh # Stop containers (preserves data)
├── test-readonly-tree.sh # Asserts nothing above ever gets written to
└── update.sh
/var/data (writable)¶
/var/data/
├── vereign
│ ├── .env # Environment variables (generated by install.sh)
│ ├── customer-config.sh # Customer-specific settings (copied from the template)
│ ├── secrets/ # Created on first run
│ │ ├── vault-keys.json # Vault unseal keys (BACK THIS UP!)
│ │ └── signing-key.csr # S/MIME certificate signing request
│ ├── tls/ # Generated TLS cert/key served by Caddy
│ ├── keycloak/ # Generated Keycloak config
│ ├── apisix/ # Generated APISIX config
│ └── update.log # Output of every update.sh run (newest at the end)
├── backups/ # Full backups
│ └── *.tar.gz
├── postgres/
├── vault/
├── seaweedfs/
├── stalwart/
├── clamav/
├── loki/
├── alloy/
├── textfile_collector/
└── dashboard_cache/
Quick Health & Log Checks¶
Run the comprehensive health check
This checks:
- All container statuses (running, healthy)
- Liveness endpoints (smimekeys-client, policy, irisagent, mxengine)
- Vault seal status
- PostgreSQL connectivity and all 4 databases
- MinIO health
- WireGuard tunnel status and peer handshakes
- Stalwart MTA (running, port 25, port 10026)
- Prometheus metrics endpoints
- Disk and memory usage
For manual log inspection:
Check logs (last 10 lines)
docker logs stargate-smimekeys-client --tail 10
docker logs stargate-policy --tail 10
docker logs stargate-irisagent --tail 10
docker logs stargate-mxengine --tail 10
Follow logs in real-time
Check all container statuses
Follow all containers logs in real-time
docker ps -a --format '{{.Names}}' | xargs -I {} sh -c 'docker logs --timestamps -f {} 2>&1 | sed "s/^/[{}] /"'
Provide logs to support¶
You can provide logs to our support via pastebin.hin-infra.ch, and CLI command:
Upload all containers logs:
Use our script:
Or execute manually:
docker ps -a --format '{{.Names}}' | xargs -I {} sh -c 'docker logs --timestamps {} 2>&1 | sed "s/^/[{}] /"' | curl https://pastebin.hin-infra.ch/ --data-binary @-
Tip
This operation can hit our upload limits - 20 Mb.
Use our script:
Or execute manually:
This is default
--tail 500 is the default value for our script, but you can still provide it.
Use our script:
Or execute manually:
Upload specific container logs:
Tip
This operation can hit our upload limits - 20 Mb. If it happens, try to reduce the log amount by setting a time limit or number of lines.
After that, you will receive a unique link in the format https://pastebin.hin-infra.ch/<20 symbols> that you can provide to support / ticket.
Warning
The expiration time is set to 30 days. If some parts of the logs or the logs themselves need to be saved for a longer period, please make sure you keep a copy of them.
Helm Deploy¶
Info
Coming soon
This file contains instructions for Helm deploy of the HIN MGW instance.
Migrating a Compose Deployment to a bootc Appliance¶
This guide walks through moving an existing standard-Compose Stargate installation (a git clone on a plain VM) onto a new bootc appliance VM - the immutable, image-based deployment that runs the same Compose stack read-only from /usr with all state on a dedicated /var/data Data Disk.
Migration reuses the same backup/restore tooling used for disaster recovery: back up the old VM, provision the new appliance, and restore the archive onto it. The one step no script can do for you is re-pointing the CA-side WireGuard registration at the new VM's public IP - budget time for that with your HIN contact.
Restore replaces ALL data on the target
restore.sh resets Vault, PostgreSQL, and object storage on the machine it runs on, then re-imports everything from the archive. Only run it against the new appliance, never against a VM you still need.
Overview¶
- On the old VM: back it up with
./scripts/backup.sh --label pre-bootcand copy the archive off the machine. - Provision and boot the bootc appliance - it fresh-installs itself and comes up healthy but empty.
- Copy the archive onto the appliance's restore drop zone,
/var/data/restore/. - Run
restore.sh --yesagainst the archive - this resets the appliance's fresh install and re-imports the old VM's data. - Update the CA-side WireGuard peer registration to the appliance's new public IP (required manual step, done with your HIN contact).
- Verify the stack is healthy, mail flows, and certificate issuance works.
Step 1: Back up the old VM¶
On the VM you are migrating from:
cd stargate-deployment/docker-compose # or /root/stargate-deployment/docker-compose
./scripts/backup.sh --label pre-bootc
backup.sh prints the archive's full path in its own summary output (Archive: ...) - read it from there rather than assuming a location. Depending on how old the install is, the archive lands either under /var/data/backups/ (already relocated per the Data Layout doc) or the older docker-compose/backups/ path. Either way, the printed path is authoritative - use the exact filename it prints, not a guessed one.
The label is appended as a suffix to the timestamp, e.g. 20260814_153000_pre-bootc.tar.gz - the commands below use *_pre-bootc.tar.gz accordingly.
Copy the resulting .tar.gz off the old machine to somewhere safe (your workstation, a jump host, or object storage) before decommissioning it:
Note
The archive contains Vault unseal keys, Vault KV secrets, the WireGuard private key, and database dumps - all unencrypted. backup.sh already chmod 600s it; keep that permission intact wherever you stage it next.
Step 2: Provision and boot the bootc appliance¶
Deploy the bootc appliance image through your usual channel (see the VM Images catalog and the platform-specific install guides under Server install > VM deployment). On first boot the appliance provisions itself automatically: Docker is already baked in, install.sh runs once to generate a fresh customer-config.sh/.env, and the Compose stack comes up healthy - but with no customer data. There is no S/MIME certificate, no configured mail domain, and no WireGuard peer for this new instance yet.
Wait until the appliance reaches that healthy-but-empty baseline before continuing:
Step 3: Copy the archive to the restore drop zone¶
init-data-layout.sh creates /var/data/restore/ on every install (including the appliance's own first boot) as the designated drop zone for restore archives. Copy the archive there:
Step 4: Run the restore¶
On the appliance:
--yes (or -y) skips the interactive confirmation prompt, which is required here since restore.sh refuses to run non-interactively without it.
This replaces all data on the appliance: it stops the running (fresh) stack, resets /var/data/{vault,postgres,seaweedfs}, then re-imports from the archive - the full PostgreSQL dump, Vault unseal keys and KV secrets, S3/object-storage contents, S/MIME CSR and certificates, and customer-config.sh (including the WireGuard private key). IP-derived settings such as KEYCLOAK_PUBLIC_URL, DASHBOARD_PUBLIC_URL, and MXENGINE_PUBLIC_ADDRESS are re-derived from the new appliance's own IP, not copied from the old VM. On success, restore.sh moves the consumed archive to /var/data/restore/restored/ so a re-run won't trigger on it again.
Step 5: CA-side WireGuard re-registration (required manual step)¶
The appliance has a new public IP. restore.sh restores the old WireGuard private key (so the tunnel's identity is unchanged), but the corresponding peer record on the CA side - endpoint, allowed IPs, and WireGuard IP - still points at the old VM's IP address. Until that record is updated, the WireGuard tunnel to HIN will not come up, and certificate issuance will fail.
This step cannot be automated by the appliance or the customer
Contact your HIN network operations / account contact and provide the new public IP (and the WireGuard port, if changed) so they can update the peer registration - endpoint, allowed IPs, and WireGuard IP - to match the new appliance. Do this as part of the migration window; mail flow and certificate renewal are blocked until it's done.
Step 6: Verify¶
Confirm:
- All containers report
healthy. - The WireGuard tunnel is up and handshaking (shown by
health-check.sh -v). - A test message flows through the gateway end to end (send through the appliance, confirm sealed delivery at the recipient).
- Certificate issuance succeeds - check the S/MIME certificate status in the dashboard, or confirm the CSR/certificate exchange over the tunnel completed.
Pre-staged migration (becomes turnkey once the bootc first-boot hook lands)¶
Instead of waiting for the appliance to fresh-install and then running Steps 3-4 by hand, you can drop the backup archive into /var/data/restore/ before first boot completes (for example, by staging it on the Data Disk during provisioning). Today this still requires manually invoking restore.sh --yes once the appliance's own first-boot install has finished, exactly as in Steps 3-4 above - the first-boot sequence does not yet look for a pre-staged archive on its own.
A planned first-boot hook (tracked in the almalinux-bootc project's Data Disk plan) will make this automatic: if an archive is already present in /var/data/restore/ at first boot, the appliance will run restore.sh --yes <archive> in place of the fresh install.sh, skipping the empty-baseline install entirely since there's nothing to reset yet. Once that lands, a pre-staged migration collapses to Steps 1-2 (back up, stage the archive, boot) plus the still-manual Step 5 (CA-side WireGuard re-registration), which remains a required step regardless of how the restore was triggered.
VM deployment
Stargate Azure deployment using an image¶
Deploy Stargate on Azure
Azure Port 25 (SMTP) Requirements¶
Warning
Before beginning installation on Microsoft Azure, review the following requirements related to outbound SMTP connectivity on port 25. Skipping this step may result in email delivery failures after installation.
Whether port 25 is available depends on your Azure subscription type:
Enterprise Agreement (EA) or MCA-E - Outbound SMTP on port 25 is not blocked. Note that external domains may still reject emails - this is outside Azure's control.
Enterprise Dev/Test - Blocked by default, but can be removed. To request removal, go to Diagnose and Solve > Cannot send email (SMTP-Port 25) in the Azure Virtual Network resource in the Azure portal.
All other subscription types - Blocked and cannot be unblocked.
Reference: Troubleshoot outbound SMTP connectivity in Azure
Get the image file¶
- Download the latest VHD image file. Please refer to VM Catalog
Upload the Azure VHD image file¶
- Navigate to https://portal.azure.com/#home
- Click Storage accounts.
- Select the storage account to use or create a new one.
- Click Block service and then Containers.
- Select the Container to upload the file to or create a new one if you do not have a Container.
- Click Upload and choose the VHD image file.
- Make sure that the Blob type is Page Blob.
Create the image¶
- Navigate to https://portal.azure.com/#home
- Click Images.
- Click Create.
- Choose the Resource group to be used or create a new one.
- Type a Name for the image.
- Choose OS type Linux and VM generation Gen 2
- At Storage blob, click browse and select the newly uploaded VHD image.
- Click Review and create.
- Click Create.
Create a VM¶
- Navigate to https://portal.azure.com/#home
- Click Virtual Machines.
- Click Create, and choose Virtual Machine from the drop-down menu.
- Choose the Resource group.
- Type a Name for the VM.
- At Image, click "See all images", click "My Images" and choose the new image that was created.
- Choose the VM size.
- Choose authentication type.
- Click Next: Disks
- Select OS disk size at least 20 GiB. Please refer to Server Requirements.
- Click Review + create
- Click Create
Find the public IP address of the new VM and add inbound firewall rules¶
- Navigate to https://portal.azure.com/#home
- Click Virtual Machines.
- Click on the new VM.
- You can see the public IP address under "Primary NIC public IP"
- Scroll down to Networking and click on it
- Click + Create port rule, Inbound port rule, Destination port ranges 25, Protocol TCP, name it SMTP. Repeat for the other required inbound port — 19818 (WireGuard). See Server Requirements → Inbound Network Access for the full list.
Attach the Data Disk first
Before the first boot, attach a second, blank disk of at least 30 GB. On first boot the appliance formats it as the Data Disk (VEREIGN-DATA, mounted at /var/data) and keeps all config and data there; without it the boot fails and rolls back. See Installation guide → Step 4.
Install HIN Gateway¶
After the VM has been successfully created, proceed with the installation and onboarding steps as described in the provided instructions
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.
Stargate VMware ESXi deployment using an image¶
Deploy Stargate on VMware
Get the image file¶
- Download the latest OVA (or OVF and VMDK if you prefer) image file. Please refer to VM Catalog
Navigate to the ESXi web UI¶
- Click Virtual Machines
- Click Create/Register VM
- Choose "Deploy a virtual machine from an OVF or OVA file"
- Click Next
- Type a name for the VM
- Click Next
- Click to select files and choose the OVA image file (or OVF and VMDK if you prefer)
- Click Next
- Choose a storage to use
- Click Next
- Choose Network and Disk for provisioning
- Click Next
- Click Finish
Data Disk included
The OVA already contains the appliance's Data Disk (VEREIGN-DATA, mounted at /var/data) — there is no extra disk to attach. See Installation guide → Step 4.
Install HIN Gateway¶
After the VM has been successfully created, proceed with the installation and onboarding steps as described in the provided instructions
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.
Proxmox deployment using an image¶
Deploy Stargate on Proxmox
Get the image file URL¶
- Please refer to VM Catalog for a list of images with URLs.
- Copy URL to clipboard, for example
https://images.hin.ch/vm-images/hingateway_v0.0.0.x86_64.qcow2
Import the image file in Proxmox¶
- In Proxmox WebUI, navigate to the Storage menu and click Import
- Click Download from URL, paste the copied URL and click "Query URL".
- Click Download and wait for "TASK OK" to appear at the end of the output log.
- Close the Task Viewer Download window.
Create a VM¶
- Click "Create VM"
- Type a name for the VM
- Click "Next"
- Choose "Do not use any media"
- Click "Next"
- Click "Next"
- Click the "Trash icon" next to "scsi0" to remove it.
- Click "Import" and under "Select Image", choose the newly imported image file.
- Click "Next"
- Select 4 CPU cores and choose your CPU Type (or use "host"). Please refer to Server Requirements.
- Click "Next"
- Select 8192 MiB Memory. Please refer to Server Requirements.
- Click "Next"
- Click "Next"
- Wait until the VM creation process finishes and then click on the new VM, click "Console", click "Start Now"
Attach the Data Disk first
Before the first boot, attach a second, blank disk of at least 30 GB. On first boot the appliance formats it as the Data Disk (VEREIGN-DATA, mounted at /var/data) and keeps all config and data there; without it the boot fails and rolls back. See Installation guide → Step 4.
Install HIN Gateway¶
After the VM has been successfully created, proceed with the installation and onboarding steps as described in the provided instructions.
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.
Windows 11 Pro deployment using an image¶
Deploy Stargate on Windows Pro (non-Pro versions do not support Hyper-V)
Install Hyper-V¶
- Click the Start button, then type "Turn Windows features on or off"
- Click on that button
- Check Hyper-V and click "OK"
- After the installation completes, click "Restart now" and wait for Windows to boot again
Note: We recommend deploying the VM using Hyper-V Generation 2
Get the image¶
- Download the .vhdx image file. Please refer to VM Catalog
Import the image file and create a VM with it¶
- Click the "Start" button and type "Hyper-V Quick Create"
- Click on that icon
- Choose "Local installation source"
- Uncheck "This machine will run Windows"
- Click "Change installation source", navigate to the downloaded .VHDX image and click on it
- Click "Create virtual machine"
- Click "Edit settings"
- Under "Memory", choose "RAM" 8192 MB. Please refer to Server Requirements.
- Under "Processor", choose "Number of virtual processors" 4. Please refer to Server Requirements.
- Click "OK"
- Click "Connect"
- Click "Start"
Attach the Data Disk first
Before the first boot, attach a second, blank disk of at least 30 GB. On first boot the appliance formats it as the Data Disk (VEREIGN-DATA, mounted at /var/data) and keeps all config and data there; without it the boot fails and rolls back. See Installation guide → Step 4.
Install HIN Gateway¶
After the VM has been successfully created, proceed with the installation and onboarding steps as described in the provided instructions
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.
Stargate Deployment on Cloudscale Using an Image¶
Get the Image File URL¶
- Refer to the VM Catalog for available images with URLs.
- Copy the
qcow2URL to your clipboard.
Import the Image File in Cloudscale¶
- In the Cloudscale WebUI, navigate to the "Custom Images" menu and click "Import a Custom Image".
- Set an appropriate Image Name.
- Define a Slug, e.g., "stargate".
- Paste the Stargate image URL into the Download URL field.
- Set Source Format to the upload format, recommended:
qcow2. - Configure additional settings as needed.
- Click Import.
Create a VM¶
- Navigate to Servers and click Launch a new Server.
- Enter your preferred FQDN or hostname.
- Under Operating System, select Custom Images and choose your imported image.
- Under Compute Flavor, select Flex-4-2 or Flex-8-2 depending on expected load (can be adjusted later). See Server Requirements for details.
- Under Storage Capacity, set at least 30 GB. Please refer to Server Requirements.
- Under Server Location, select your preferred zone.
- Under Network Management, enable only IPv4 if the Stargate instance must be internet-accessible (e.g., for Office 365).
- Under Access Security, select your SSH key (usable with the
almalinuxuser). - Under Password, set any secure password you prefer.
Without a password, SSH password authentication is disabled
You must still use the initial password provided by HIN for your first login. However, if you do not set a new password, cloud-init will disable SSH password authentication for all users, allowing SSH access only via public key authentication.
- Click Launch.
Attach the Data Disk first
Before the first boot, attach a second, blank disk of at least 30 GB. On first boot the appliance formats it as the Data Disk (VEREIGN-DATA, mounted at /var/data) and keeps all config and data there; without it the boot fails and rolls back. See Installation guide → Step 4.
Install HIN Gateway¶
After the VM has been successfully created, proceed with the installation and onboarding steps as described in the provided instructions
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.
VM Images Catalog¶
Here you can find a current VM Catalog for different platforms. Please do not forget to check the SHA-256 hash of downloaded images. You can use the https://images.hin.ch/vm-images/SHA256SUMS file to compare it.
How to perform SHA256 hash check locally
You can calculate the SHA256 hash of downloaded files with the following command and then compare it with the values in the table below.
Open Terminal and execute:
As a more advanced variant, you can execute the following command and paste the predefined checksum as SHA256_VALUE and the file name as IMAGE_NAME:
| Image Name | Image Type | Image Size | Link | SHA256 Checksum |
|---|---|---|---|---|
hingateway-v1.1.7.x86_64.mf |
4.0K / 364 bytes |
Download | caafd4817e8c6c397f24d3ef284b774c9282a3300e77ea33727180203a813bf3 |
|
hingateway-v1.1.7.x86_64.ova |
3.3G / 3496058880 bytes |
Download | 6617a8fc37aba7571bad277c3d14f53885154114c08b34809102e78864188965 |
|
hingateway-v1.1.7.x86_64.ovf |
12K / 9322 bytes |
Download | 47b1297b724ecdf575ce350175c2513d25e8daa3aa79f34f4962e43eb97f9d50 |
|
hingateway-v1.1.7.x86_64.qcow2 |
3.3G / 3526492160 bytes |
Download | f4d4b9c2994b8e674e6f649a249fe3d6e0345593e0e02f588472c199ce67582d |
|
hingateway-v1.1.7.x86_64.raw |
30G / 32212254720 bytes |
Download | 583763ab21804cf312b4bffa8c25c1c1b05990be5b76f0044d9804b2c5eeec2e |
|
hingateway-v1.1.7.x86_64.raw.gz |
3.3G / 3475192756 bytes |
Download | f5c37aaa231dd7951f30d13cec7a783e55adaffe4b2db2853cc98a12c381d571 |
|
hingateway-v1.1.7.x86_64.vhd |
31G / 32212255232 bytes |
Download | 100c2f3528665ad91ba2a2915634032472c8a0292e519d923dc2a6107beebeb9 |
|
hingateway-v1.1.7.x86_64.vhd.gz |
3.3G / 3475192835 bytes |
Download | 2aa3bc86d7c2b8b3a02d1dc462096e372abdf73452301decfd2ce44501db3ca4 |
|
hingateway-v1.1.7.x86_64.vhdx |
4.7G / 5007998976 bytes |
Download | 71a8f84014d1ecc34c3843b5b45ec2dd67f11fafbf0eb9f83164d56bb8da098a |
|
hingateway-v1.1.7.x86_64.vmdk |
3.3G / 3491979776 bytes |
Download | 4a5a0fd6950cac3ed539fe2834d3b84742db01462a8f93764965932cb273237e |
|
SHA256SUMS |
4.0K / 979 bytes |
Download | f0fa4b8957cc007f4a91ecd0fa02d641f79e8285b74d4884d626be9c5b2fa611 |
Troubleshooting & Diagnostics¶
A structured guide to diagnosing a Stargate appliance from the command line: what to check, where the logs are, and safe recovery actions.
Where the scripts are
The helper scripts live in the deployment directory under scripts/. Commands below use the full path for VM images: /usr/share/stargate-deployment/docker-compose/scripts/. If you installed elsewhere, substitute your own install directory (the folder that contains docker-compose.yml and scripts/).
docker compose ... commands must be run from the deployment directory:
Where the writable state is
The docker-compose/ tree above (scripts, docker-compose.yml, config templates) is read-only at runtime. All writable state - .env, customer-config.sh, secrets/, generated TLS/Keycloak/APISIX config, backups, and every service's own data - lives under /var/data instead (STARGATE_DATA_DIR overrides this root for testing). In particular: config/secrets are under /var/data/vereign/ and backups under /var/data/backups/. See Data Layout for the full breakdown. Prefer the wrapper scripts (./scripts/start.sh, ./scripts/stop.sh, ...) over a bare docker compose up -d, which won't pick up /var/data/vereign/.env on its own.
1. Start here: the health check¶
One command summarizes the whole appliance:
It reports pass/fail for: containers (running/healthy), liveness endpoints (smimekeys, policy, irisagent, mxengine), Vault seal status, PostgreSQL connectivity + databases, SeaweedFS, the WireGuard tunnel + peer handshakes, Stalwart MTA (ports 25 / 10026), Prometheus metrics endpoints, and disk / memory.
Tip
Run this first. A single FAIL line usually points you straight at the section below.
2. Where the logs are¶
| Layer | Command | What it shows |
|---|---|---|
| Boot / first-install / auto-start | sudo journalctl -u stargate -n 200 --no-pager |
The systemd service that runs start.sh on boot and the first-boot install |
| Updates (VM appliance) | hingateway-status, docker logs stargate-ops-agent --tail 100 and journalctl -u greenboot-healthcheck |
Booted, staged and rollback OS versions; backup, download and errors of an update (only until the next reboot); result of the boot health check |
update.sh runs |
cat /var/data/vereign/update.log |
Output of every update.sh run, newest at the end: plain Compose updates, dashboard updates on older pre-bootc VMs, and runs after changing customer-config.sh |
| A single service | docker logs stargate-<service> --tail 100 |
e.g. stargate-dashboard, stargate-mxengine, stargate-keycloak |
| Follow one service live | docker logs -f stargate-mxengine |
Real-time |
| All containers live | docker ps -a --format '{{.Names}}' \| xargs -I{} sh -c 'docker logs --timestamps -f {} 2>&1 \| sed "s/^/[{}] /"' |
Merged, prefixed by container |
| Web log viewer | Dozzle at https://<SERVER_IP>:8190 (Keycloak login) |
Browse all container logs in a UI |
To hand logs to HIN support, use the upload script: it collects the last N log lines from every service (plus host and version info), uploads them, and prints a link to share - see Provide logs to support:
/usr/share/stargate-deployment/docker-compose/scripts/send-logs-to-support.sh --tail 5000 # last 5000 lines from each service
# other options: --since 1h | --until 5m | --all (no argument = --tail 500)
The upload is capped at 20 MB, so on a busy appliance prefer --tail/--since over --all.
3. Containers not running or restarting¶
Read the Status column:
| Status | Meaning | Action |
|---|---|---|
Up ... (healthy) |
Running fine | - |
Up ... (no health) |
Running; no healthcheck defined | Check its docker logs if you suspect trouble |
Restarting |
Crash-looping | docker logs stargate-<svc> - fix the root error (config, secret, dependency) |
Exited (0) |
One-shot init finished OK (e.g. *-init, vault-data-fixer) |
Normal |
Exited (1+) |
Failed | docker logs stargate-<svc> - the last lines show why |
Created |
Never started - a dependency didn't come up | Check what it depends_on (usually Postgres/Vault); fix that first |
Restart a single service (safe, non-destructive):
docker compose up -d <service> # recreate one service
docker compose restart <service> # just restart it
Startup order
Services wait on their dependencies (depends_on + healthchecks). During a full restart, brief connection refused / database system is starting up lines while Postgres/Vault come up are normal and clear within a minute.
4. Diagnose by symptom¶
Dashboard or Keycloak won't load / can't log in¶
- Both are fronted by Caddy: Dashboard on
:443, Keycloak on:8180. - Check the chain:
docker logs stargate-caddy,stargate-dashboard,stargate-keycloak,stargate-apisix. - Keycloak must be healthy before the dashboard works:
docker compose ps keycloak. - TLS warning in the browser is expected (self-signed cert) - accept and proceed.
- Login redirects failing usually mean the public URL doesn't match how you reach the box - verify
KEYCLOAK_PUBLIC_URL/DASHBOARD_PUBLIC_URLin.envpoint at the IP/host you actually use.
WireGuard tunnel down / certificate issuance fails¶
This is the most common issue - certificates fail when the tunnel is down, so always fix the tunnel first.
/usr/share/stargate-deployment/docker-compose/scripts/health-check.sh -v # shows WireGuard peer + handshake status
docker logs stargate-irisagent | grep -iE "handshake|peer|cert|wireguard"
- Confirm the firewall allows
19818(UDP and TCP) inbound/outbound. - Verify the peer is registered on the HIN side (support step) - you supply WG public key,
DEPLOYMENT_NAME,SERVER_STATIC_IP,WG_INTERFACE_PORT. - Once the tunnel shows a recent handshake, retry certificate issuance from the dashboard.
Vault sealed or init failed¶
- Vault must be unsealed for smimekeys/mxengine/policy to work. Keys live in
/var/data/vereign/secrets/vault-keys.json. - If
vault-initexited non-zero, the keys file may be missing/corrupt - check its logs; re-running/usr/share/stargate-deployment/docker-compose/scripts/init-vault.shre-attempts unseal.
Do not delete /var/data/vereign/secrets/vault-keys.json
Losing it means losing access to all stored secrets. Keep a backup.
PostgreSQL / database connectivity¶
- Transient
the database system is starting up (57P03)right after a restart is normal - services reconnect automatically. - Persistent auth failures usually mean
POSTGRES_PASSWORDin.envdrifted from the data volume - see the update/secrets notes, and avoid editing it by hand.
Mail not flowing¶
-
Inbound arrives on
:25(Stalwart). Many cloud providers block port 25 by default:If
25is blocked, request an exception from your provider. - Outbound / sealing goes Stalwart → mxengine (:8084HTTP, SMTP:1587):docker logs stargate-mxengine. - Mail loops show as the same message cycling - check that your domain's MX does not resolve back to this appliance's own IP. - See Mail relay setup and DNS setup for the expected routing.
An update failed¶
On the VM appliance, an update from the dashboard is run by the ops-agent in three phases: a backup (backup.sh), the download of the target OS image (bootc switch), and the reboot into it. Start with:
docker logs stargate-ops-agent --tail 100 # the update orchestrator: backup, download, errors
hingateway-status # booted, staged and rollback OS versions
The ops-agent container is recreated on every boot, so its log covers an update only until the VM restarts. After a reboot, use hingateway-status and journalctl -u greenboot-healthcheck instead.
- Failed while backing up. The ops-agent cancels the update when the pre-update backup fails, so nothing has been changed. Check the error in the ops-agent log and the free space on the Data Disk (
df -h /var/data), then start the update again. - Failed while downloading. The dashboard's update status shows the error as
bootc switch: ...; the ops-agent log only recordsos select failedwith the exit status. Usually the VM cannot reach the image registry (check outbound internet access and DNS) or the OS disk is full (df -h /sysroot). The running version is not affected; start the update again once the cause is fixed. - Downloaded, but the version did not change. The new version is activated only by a reboot. If you chose
Later, restart the VM (sudo systemctl reboot) and checkhingateway-statusagain. If the VM did reboot and still shows the old version as booted, it rolled back automatically - see the next item. - Automatic rollback after a failed boot. After booting a new version, health checks run - among others, whether Docker is running, the Data Disk is mounted and the Stargate stack is healthy, plus the operating system's default checks. If a required check fails, the boot is marked RED and the VM boots the previous version again on its own.
journalctl -u greenboot-healthcheckshows which check failed. Fix the cause before you try the update again; rebooting alone repeats the same rollback. If the new version had already changed a database schema, or the failed update was the first upgrade onto an image with Data Disk support (the stack then shows an empty/var/data, but your data is untouched), read the rollback warnings before you do anything else. - The new version boots but is unhealthy. Check the boot health check with
journalctl -u greenboot-healthcheckand the stack with/usr/share/stargate-deployment/docker-compose/scripts/health-check.sh -v. To return to the previous version, useRoll backon the dashboard or runhingateway-rollback && sudo systemctl reboot- read the rollback warnings first. - After a successful update, confirm the application versions:
/usr/share/stargate-deployment/docker-compose/scripts/gather-app-versions.sh(or checkdocker compose psimage tags). - If a single service is stuck after an update, recreate only that service:
cd /usr/share/stargate-deployment/docker-compose && sudo docker compose --env-file /var/data/vereign/.env up -d --no-deps --force-recreate <service>.sudois needed because.envis readable only by root, and--no-depskeeps Compose from also recreating the service's dependencies, such as Vault, which would come back sealed. If/var/data/vereign/docker-compose.override.yml(or.yaml) exists, add-f docker-compose.yml -f <override file>after--env-file /var/data/vereign/.envso the override stays applied. Services that belong to a profile need it as well: add--profile dozzlefordozzleandoauth2-proxy,--profile keri-idagentforidagentand--profile keri-watcherforwatcher.
On a plain Compose install (a git clone you manage yourself) there is no ops-agent update: you run update.sh yourself, and its output is appended to /var/data/vereign/update.log, newest run at the end. See Plain Compose install for the update steps.
Warning
Do not try to repair an update on the VM appliance with git or update.sh --release. /usr/share/stargate-deployment is part of the read-only OS image and there is no repository checkout to reset; versions change only by switching the OS image.
Older VMs (before the bootc image)¶
VMs installed from an image older than the bootc appliance have a git checkout under /usr/share/stargate-deployment instead of a read-only OS image. The VM appliance steps above do not apply to them. The long-term fix is to move them to a bootc appliance as described in Migrating a Compose Deployment to a bootc Appliance. Until then, the following applies to these older VMs only. On them the ops-agent runs update.sh, whose output is appended to /var/data/vereign/update.log (newest run at the end) - check it first when an update fails.
Update starts but nothing happens (updating from an older version). If the ops-agent log stops at pulling deployment repo ... and the update never proceeds, the repository on the VM most likely has local edits to a tracked file (commonly a hand-patched docker-compose.yml). That makes the ops-agent's git checkout refuse to run, so the update stalls. Force-reset the repository to the latest revision, then re-run the update. Git is the single source of truth; this discards local edits to tracked files only - customer-config.sh, .env, and secrets/ live under /var/data/vereign/, outside the repository checkout, and are always preserved:
cd /usr/share/stargate-deployment
sudo git fetch origin
sudo git checkout -f main
sudo git reset --hard origin/main
cd docker-compose
sudo /usr/share/stargate-deployment/docker-compose/scripts/update.sh
update.sh regenerates .env, pulls the images, and recreates the affected services. If the release changed configuration files, run sudo /usr/share/stargate-deployment/docker-compose/scripts/start.sh afterwards so every container picks up the new files. When it completes, retry the update from the dashboard; it will now proceed.
Warning
Do not use git pull here. On a working tree with local edits it aborts with "local changes would be overwritten", which forces a git stash / merge-conflict / manual-recovery detour. The git checkout -f + git reset --hard sequence above avoids that entirely and is the safe, repeatable way to bring the repo current.
VMs on v0.5.1¶
The following steps update a VM from v0.5.1 to v0.5.3. On v0.5.x, .env, customer-config.sh and secrets/ are inside the checkout in /usr/share/stargate-deployment/docker-compose/, not under /var/data/vereign/. Log in to the VM with the Linux administrator account.
- Edit
/usr/share/stargate-deployment/docker-compose/.envand set the ops-agent version to v0.0.3. - Edit
/usr/share/stargate-deployment/docker-compose/customer-config.shand set the ops-agent version to v0.0.3 there as well. - Fetch the release tags:
cd /usr/share/stargate-deployment && sudo git fetch --tags origin - Check out the target release:
sudo git checkout v0.5.3. Do not check outmain: it contains the current scripts, which expect the newer/var/datalayout. - Update the ops-agent container:
cd docker-compose && sudo docker compose up -d ops-agent - Log in to the Dashboard.
- Navigate to Settings.
- In the Update section at the bottom of the page, enter the target version (v0.5.3) and start the update process.
After that, move the VM to a bootc appliance as described in Migrating a Compose Deployment to a bootc Appliance.
Dozzle (log viewer) not reachable¶
- URL is
https://<SERVER_IP>:8190; it requires a Keycloak login (same realm as the dashboard) via oauth2-proxy. - It only runs when
DOZZLE_ENABLED="true". Check:docker compose ps dozzle oauth2-proxy. - Ensure the firewall allows
:8190inbound. See Monitoring and Logs.
Onboarding: entering the activation code returns an error¶
If the activation code is rejected, check in order:
- Wrong code - it wasn't copied in full (a truncated copy-paste, an extra space, or a missing character). Re-copy the complete code and re-enter it.
- Code already used - it was already consumed by a previous onboarding. Request a fresh code.
- WireGuard / connectivity to HIN - the
irisagenttunnel isn't up, so the code can't be validated against HIN. See WireGuard tunnel down above (/usr/share/stargate-deployment/docker-compose/scripts/health-check.sh -v,docker logs stargate-irisagent). - No domain tied to the registration - the customer's HIN registration has no domain associated with it, so there is nothing to activate. This is resolved on the HIN side.
Onboarding: activation code accepted, but no domains are listed¶
Most likely cause: the WireGuard tunnel is not established - usually a wrong public IP or a firewall port not open. Without the tunnel the appliance cannot fetch the domain list from HIN.
- Verify
SERVER_STATIC_IPincustomer-config.shmatches the real public IP. - Confirm
19818(UDP and TCP) is open inbound/outbound. - Confirm the peer is registered on the HIN side for this IP (support step).
docker logs stargate-irisagentshould show a recent handshake; if not, fix the tunnel first (see WireGuard tunnel down above).
Changing the server IP address (initial setup only)¶
If the server IP was wrong or unset at first boot, reset cleanly and reinstall. Save the configuration first, because purge.sh also deletes customer-config.sh:
/usr/share/stargate-deployment/docker-compose/scripts/backup.sh --config-only # saves customer-config.sh
/usr/share/stargate-deployment/docker-compose/scripts/purge.sh # destroys ALL data - see warning below
/usr/share/stargate-deployment/docker-compose/scripts/restore.sh --config-only /var/data/backups/<timestamp>_config-only.tar.gz
nano /var/data/vereign/customer-config.sh # set SERVER_STATIC_IP=<NEW IP>
reboot # installs again with the new IP
backup.sh prints the exact restore command, and the archive stays in /var/data/backups, which purge.sh does not delete. The reboot runs the installation and also reloads the locally built images that purge.sh removes. On a plain Docker installation, run install.sh instead of rebooting.
The TLS certificate and several service URLs are derived from the IP at first boot, so a purge + reinstall regenerates them for the new address. The restored file keeps the generated passwords and the WireGuard key. If the gateway is already registered at HIN, the registration has to be updated with the new IP.
Only before onboarding
purge.sh permanently deletes all data - databases, Vault, and the S/MIME keys. This is safe only on a fresh, not-yet-onboarded appliance. Never run purge.sh to change the IP of a live/onboarded gateway - it causes data loss and mail that can no longer be decrypted. For a production IP change, contact support.
5. Storage & disk¶
df -h / # is the disk full?
docker system df # space used by images / containers / volumes
du -sh /var/lib/docker/volumes/* # per-volume usage (Postgres, SeaweedFS, Loki, ...)
- Container logs are capped (json-file, 100 MB × 5 per container) so they shouldn't fill the disk, but images and volumes can.
- Reclaim space safely:
docker image prune -af(removes unused images only). Avoiddocker system prune --volumes- it deletes data volumes. - Object storage is SeaweedFS (
stargate-seaweedfs):docker logs stargate-seaweedfs --tail 50.
6. VM resources¶
free -h # memory (min 8 GB)
nproc # CPUs (min 4)
docker stats --no-stream # per-container CPU/RAM
uptime # load average
Host metrics are also exported for Prometheus on :9100/metrics (see Monitoring). If the box is swapping or pegged, expect healthchecks to flap and updates to be slow.
7. Network & ports¶
Quick reachability check for the key inbound ports:
| Port | Service | Direction |
|---|---|---|
25 |
Stalwart SMTP (inbound mail) | inbound |
443 |
Dashboard (HTTPS) | inbound |
8180 |
Keycloak | inbound |
8190 |
Dozzle (optional) | inbound |
19818 |
WireGuard (UDP and TCP) | in/outbound |
Outbound access is needed to the container registry, the S/MIME CA (over the WireGuard tunnel), and any remote Loki you configured. See the full port table on the home page and Applications overview.
The Docker network overlaps the local network¶
The services talk to each other on an internal Docker network. Docker picks its address range when the stack is created, and avoids the networks the VM is directly connected to at that moment. If the range still contains addresses from your network, the appliance cannot reach those machines (for example a mail or DNS server), and they cannot reach it. The health check reports this under Docker network:
[FAIL] stargate-network 172.18.0.0/16 overlaps host routes: 172.18.5.0/24 dev ens18 - see Troubleshooting: The Docker network overlaps the local network
- The IP was changed after the first boot - Docker chose the range while the VM still had its first address. On a gateway that is not onboarded yet, use the purge + reinstall from Changing the server IP address above; Docker then picks a free range by itself. On an onboarded gateway, recreate the network as shown below.
-
The overlapping network is behind a router (a static route, or a network at another site) - Docker does not avoid such networks. The health check sees every route on the host, but not networks reached through the default gateway. First give Docker a free range: add this to
/etc/docker/daemon.json, keeping any settings already in the file, and use a range your network does not use:Then run
systemctl restart dockerand recreate the network as shown below.
Recreating the network keeps all data in /var/data. Stop the service first, otherwise a running Dozzle keeps the old network in use:
systemctl stop stargate
cd /usr/share/stargate-deployment/docker-compose
docker compose --env-file /var/data/vereign/.env down
systemctl start stargate
/usr/share/stargate-deployment/docker-compose/scripts/health-check.sh
8. Recovery actions¶
Ordered least- to most-disruptive:
docker compose up -d <service> # recreate one stuck service
sudo systemctl restart stargate # restart the whole stack (via start.sh)
/usr/share/stargate-deployment/docker-compose/scripts/stop.sh && /usr/share/stargate-deployment/docker-compose/scripts/start.sh
Backups & destructive recovery
/usr/share/stargate-deployment/docker-compose/scripts/backup.sh and /usr/share/stargate-deployment/docker-compose/scripts/restore.sh handle data backup/restore. /usr/share/stargate-deployment/docker-compose/scripts/purge.sh deletes all data (databases, Vault, storage) for a clean reinstall - use only as a last resort and only with a current backup. Details: Docker Advanced configuration.
9. When to contact support¶
If the health check still shows failures after the steps above, open a ticket via Support / Contact us and include:
- The appliance version (
/usr/share/stargate-deployment/docker-compose/scripts/gather-app-versions.sh) and customer name. - The health-check output (
/usr/share/stargate-deployment/docker-compose/scripts/health-check.sh -v). - A log bundle link from
/usr/share/stargate-deployment/docker-compose/scripts/send-logs-to-support.sh(see Provide logs to support). - What you were doing when it broke, and any screenshots.
Setup updated keycloak¶
Note: This instructions is valid if you are running on VM image v0.5.1 and then you updated to newer version.
After the latest Keycloak update, a breaking change causes authenticated users to be unexpectedly redirected to the login page when navigating to specific application routes (e.g., Peers, Peer Certificates).
To resolve this, the following manual configuration must be completed in the Keycloak UI.
Resolution Steps¶
-
Open Keycloak in your browser at
https://<VM IP address>:8180/admin/master/console/- The
:8180port is required - Keycloak is served on port 8180. Opening the IP with no port reaches the dashboard (:443) instead, which redirects you to the stargate-realm login where the admin user does not exist (this is the usual cause of "invalid username or password" / "wrong realm" here). - Log in against the master realm (the
/admin/master/console/path selects it) with usernameadmin(theKEYCLOAK_ADMIN_USERvalue - lowercase) and theKEYCLOAK_ADMIN_PASSWORDvalue from the machine's/var/data/vereign/.env(read it from the Linux console).
- The
-
In the admin console, switch the realm selector (top-left) from master to stargate:
- Go to Clients → dashboard
- Navigate to the Client scopes tab → click dashboard-dedicated
- Select Configure a new mapper → Audience
-
Set the following configurations:
- Name: apisix-audience
- Included client audience: apisix (select from dropdown)
- Included custom audience: (leave empty)
- Add to access token: On
- Add to token introspection: On
- Add to ID token / lightweight token: Off
-
Click Save
Resetting a Stargate User Password in Keycloak¶
Follow the steps below to reset the password of a Stargate user through the Keycloak administration console.
-
Connect to the HIN Gateway VM
-
Open the VM console, or connect to the HIN Gateway VM via SSH.
-
Retrieve the Keycloak administrator credentials
-
Open the
/var/data/vereign/.envfile -
Locate the following variables:
KEYCLOAK_ADMIN_USERKEYCLOAK_ADMIN_PASSWORD
-
Open the Keycloak Admin Console
- In a browser, navigate to:
http://<VM-IP>:8180/admin/master/console -
Replace
<VM-IP>with the IP address of the HIN Gateway VM. -
Sign in to Keycloak
- Enter the administrator username and password retrieved from the
.envfile. -
keep the same administrator password if you change it you must store it securely
-
Select the Stargate realm
-
From the realm selector in the Keycloak Admin Console, select Stargate.
-
Find the user
- Navigate to Users.
-
Search for and select the Stargate user whose password needs to be reset.
-
Reset the user's password
- Select the option to reset the user's password.
- Enter the new password and confirm the change.
Do not change or reset the Keycloak administrator password as part of this procedure. Any changes of admin password can lead to potention lockdown of Keycloak without anyone be able to access it!
Request Activation code¶
In case you want to install or re-install HIN Gateway instance you will need Activation code , which can be provided by HIN Support
Reset Activation code breaks peer connection
Resetting the activation code for an active instance clears the customer WireGuard peer connection.
If you request this reset for the wrong customer instance, it will delete and break that active instance connection. Before requesting a reset, double-check the exact instance and confirm all domains hosted on it.
Reboot on VM v0.6.1¶
Please keep in mind if you do a REBOOT an instance which is running on v0.6.1 it will revert back to the previous non working Stalwart version <0.0.23.
We recommend to update the instance to newer version from Settings->System version section!
Release Notes¶
v1.1.7¶
Released on 14 October 2026.
What's new and improved¶
- Outbound relays can now authenticate with a username and password for each domain.
- Inbound relays can now authenticate with a username and password for each domain.
- Outlook confidentiality label: Emails marked as confidential in Microsoft's sensitivity label field are now delivered through HIN Mail with encryption to non-HIN members.
- Time synchronization: HIN Gateway now uses NTP to keep its clock in sync with trusted time servers. NTP can be configured in the dashboard.
- Log forwarding: Logs can now be sent to Loki using an API token.
- Migration from v0.5.x: A migration script exports the S/MIME key and certificate when moving from v0.5.x to v0.6.x or above.
- Network defaults: The default DNS server is now 8.8.8.8 (previously 1.1.1.1). The DNS server can be changed during configuration.
Fixes¶
- Fixed repeated reboots (up to three in a row) after network settings were changed.
- Fixed an error during onboarding when the certificate import started before the Iris Agent sync had finished.
- Domains are now trimmed and converted to lowercase when added.
- Fixed the confirmation message after updating a peer.
- The new-version notification is now translated into all supported languages.
v0.6.2¶
Released on 24 September 2026.
This hotfix release strengthens HIN Gateway security, corrects the Stalwart version and disables spam filtering.
What’s new and improved¶
- Ships Stalwart v0.16.23, which works with the latest spam filter ruleset.
- Disables the spam ruleset and SpamFilter, so an upstream ruleset release can't break a pinned version.
Fixes¶
- Updated spam filter handling in the provisioning script and improved error logging.
- Updated the Stalwart image to v0.16.23 and added a function that removes DNSBL servers.
v0.6.1¶
Released on 4 September 2026.
This maintenance release strengthens HIN Gateway security, corrects email-header handling and improves update and migration guidance.
What’s new and improved¶
- Security: Restricted external access to internal service ports and strengthened authentication settings and permissions.
- Updates and rollback: Added instructions for updating HIN Gateway and returning to a previous version, available in English, German, French and Italian.
- Migration guidance: Updated email checks before and after migration and added translations of the multi-domain guidance.
Fixes¶
- Corrected the handling of HIN-specific email headers.
- Added the missing VMware data-disk image to the v0.6.0 downloads.
v0.6.0¶
Released on 31 August 2026.
This release adds multi-domain migration support, domain-level relay configuration, email authentication (such as DKIM and ARC) and improvements to installation, security and stability.
What’s new and improved¶
- Multi-domain setups: Multiple domains can now be configured or migrated to a single HIN Gateway. The updated guidance covers complete and phased migrations, including rollback scenarios.
- Domain-based configuration: Relay configuration per domain is now available.
- Email authentication: DKIM and ARC signing, as well as DKIM, ARC, SPF and DMARC verification, can now be configured for each domain. The required DNS records are shown directly in HIN Gateway.
- Installation and migration: Updated guidance now covers new installations, single-domain and multi-domain migrations, Exchange integration, mail routing, networking and TLS testing.
- Appliance migration: A documented process is available for moving an existing Docker Compose installation to the image-based HIN Gateway appliance.
- Backup and restore: Persistent data now uses a consistent location, with safer restore handling and warnings when backup and target versions differ.
- Diagnostics and security: Health checks, logging and diagnostic collection have been improved. Installation and recovery scripts now provide better protection for credentials and backup data.
Fixes¶
- Improved first-time initialization of Vault and WireGuard settings.
- Improved service startup on slower virtual machines.
- Fixed edge cases affecting backup and restore operations.
- Fixed inaccurate health-check results and configuration fallback handling.
Important information for appliance migrations¶
When moving an existing Docker Compose installation to the image-based appliance, follow the dedicated migration guide. Restoring a backup replaces all data on the target appliance. If its public IP address changes, HIN must update the central WireGuard registration.
v0.5.3¶
Released on 29 July 2026. Hotfix applied on 5 August 2026.
This release improves email delivery, certificate handling, the dashboard and troubleshooting. There are no changes to SEAL.
Hotfix, 5 August 2026¶
Fixed an issue in the certificate import process where the domain was not correctly linked to the corresponding HIN Gateway peer.
The fix was applied to the central services. Local virtual machine installations were not affected and did not require an update.
What’s new and improved¶
- Separate relays can now be configured for senders and recipients.
- Emails to recipients outside the HIN network can be sent through the configured SMTP relay.
- The previous hourly email-processing limit has been removed.
- Sender and recipient matching has been improved for incoming and outgoing emails.
- Domain ownership is now checked before an S/MIME certificate is issued.
- Peer certificates can now be searched and sorted.
- TLS certificates can be generated directly from the dashboard.
- Search and pagination have been added to the peer overview.
- Searching through logs is now easier.
- A warning is shown when the private key has not been imported.
- Error messages and diagnostic information have been improved.
Fixes¶
- Improved certificate selection when sending encrypted emails.
- Fixed the handling of encrypted messages signed with revoked or untrusted certificates.
- Fixed an issue with session refresh tokens.
- Fixed several errors in the setup and domain-management forms.
- Fixed country-code selection and field validation.
- Fixed an issue when regenerating an activation code.
- Fixed the processing of certain S/MIME signer information.
- Fixed missing connection information in tunnel delivery logs.
- Network settings are now correctly reset after the virtual machine’s IP address changes.
v0.5.1¶
Our initial release.
Contact us¶
Support
For any questions or issues related to the deployment and operation of the Stargate appliance, please contact HIN support by email, phone:
You can find more information in our Contact page: https://support.hin.ch/de/kontakt.cfm
Please include relevant information such as the customer name, appliance version, and screenshots/logs where applicable, to help us process your request efficiently.





































































