This guide walks you through deploying Omni on a Linux host with internet access. By the end, you will have a fully functional Omni instance running with TLS, an OIDC identity provider, and either embedded or external etcd.
Prerequisites
You will need:
- A Linux host with at least 2 vCPUs and 4 GB RAM
- A domain name or IP address for your Omni instance
- Docker installed on the host
- The following ports open and accessible:
| Port | Protocol | Purpose |
|---|
| 443 | TCP | Omni UI and API |
| 8090 | TCP | SideroLink API |
| 8091 | TCP | Event sink |
| 8100 | TCP | Kubernetes proxy |
| 5556 | TCP | Dex OIDC |
| 50180 | UDP | WireGuard |
Step 1: Install Docker
Omni and its supporting services run as Docker containers. Follow the official Docker installation guide for your Linux distribution.
After installing, add your user to the docker group so you can run Docker without sudo:
sudo usermod -aG docker $USER
newgrp docker
Step 2: Install cfssl
cfssl generates and signs the TLS certificates used by Omni and Dex.
CFSSL_VERSION=$(curl -sI https://github.com/cloudflare/cfssl/releases/latest \
| grep -i location | awk -F '/' '{print $NF}' | tr -d '\r')
curl -L -o cfssl \
https://github.com/cloudflare/cfssl/releases/download/${CFSSL_VERSION}/cfssl_${CFSSL_VERSION#v}_linux_amd64
curl -L -o cfssljson \
https://github.com/cloudflare/cfssl/releases/download/${CFSSL_VERSION}/cfssljson_${CFSSL_VERSION#v}_linux_amd64
chmod +x cfssl cfssljson
sudo mv cfssl cfssljson /usr/local/bin/
Step 3: Set environment variables
Set variables that define your host’s network addresses and the internal hostnames for Omni and Dex. They are referenced throughout the guide, so set them before proceeding.
export HOST_PUBLIC_IP=$(curl -s https://ifconfig.me)
export HOST_PRIVATE_IP=$(hostname -I | awk '{print $1}')
export OMNI_ENDPOINT=omni.internal
export AUTH_ENDPOINT=auth.internal
export OMNI_USER_EMAIL="admin@omni.internal"
echo "Public IP: $HOST_PUBLIC_IP"
echo "Private IP: $HOST_PRIVATE_IP"
If your host does not have a public IP (e.g. a local VM), set HOST_PUBLIC_IP to the IP your local machine uses to reach the host.
3.1: Add internal hostnames to /etc/hosts
Omni and Dex use .internal hostnames that are not registered in public DNS. This maps them to localhost so the host can resolve them.
echo "127.0.0.1 ${OMNI_ENDPOINT} ${AUTH_ENDPOINT}" | sudo tee -a /etc/hosts
Step 4: Generate TLS certificates
Omni and Dex communicate over TLS. In this step, you create a root CA and use it to sign a certificate covering both service endpoints. The CA certificate is also distributed to client machines in Step 9 so their browsers trust the Omni UI.
If you already have a trusted internal CA, use it to sign the certificate in Step 4.3 and skip Steps 4.1 and 4.2.
4.1: Create the root CA
Run this command to generate a self-signed root CA, the trust anchor for all certificates in this setup.
It produces ca-key.pem (private key), ca.pem (public cert), and ca.csr (signing request).
cat <<EOF > ca-csr.json
{
"CN": "Internal Root CA",
"key": { "algo": "rsa", "size": 4096 },
"names": [{ "C": "US", "O": "Internal Infrastructure", "OU": "Security" }]
}
EOF
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
Add the CA to the host’s system trust store so services running on this machine trust certificates signed by it:
sudo cp ca.pem /usr/local/share/ca-certificates/ca.crt
sudo update-ca-certificates
4.2: Create the signing configuration
Create a signing configuration that tells cfssl how to sign certificates. The web-server profile is used for Omni and Dex. The client profile is used later for external etcd authentication in Step 7.
cat <<EOF > ca-config.json
{
"signing": {
"default": { "expiry": "8760h" },
"profiles": {
"web-server": {
"usages": ["signing", "key encipherment", "server auth"],
"expiry": "8760h"
},
"client": {
"usages": ["signing", "key encipherment", "client auth"],
"expiry": "8760h"
}
}
}
}
EOF
4.3: Generate the server certificate
Generate a single wildcard certificate covering both the Omni and Dex endpoints. Both IPs are included so TLS connections work regardless of whether clients connect by hostname or IP.
cat <<EOF > wildcard-csr.json
{
"CN": "Internal Wildcard",
"hosts": [
"${OMNI_ENDPOINT}",
"${AUTH_ENDPOINT}",
"127.0.0.1",
"${HOST_PUBLIC_IP}",
"${HOST_PRIVATE_IP}"
],
"key": { "algo": "rsa", "size": 4096 }
}
EOF
Sign the certificate using the CA and configuration from the previous steps:
cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=web-server wildcard-csr.json | cfssljson -bare server
Bundle the server certificate with the CA so clients can verify the full chain:
cat server.pem ca.pem > server-chain.pem
This produces server-key.pem (private key), server.pem (public cert), and server-chain.pem (cert bundled with CA). Make them readable by all service users by running:
Step 5: Generate the etcd encryption key
Omni encrypts all data written to etcd at rest using a GPG key. This generates the key and exports it to omni.asc, which Omni reads on startup.
Do not set a passphrase on this key. Omni reads the key file on startup and cannot prompt for a passphrase interactively.
gpg --batch --passphrase '' \
--quick-generate-key \
"Omni (Used for etcd data encryption) omni@internal.local" \
rsa4096 cert never
FINGERPRINT=$(gpg --with-colons --list-keys "omni@internal.local" \
| awk -F: '$1 == "fpr" {print $10; exit}')
gpg --batch --passphrase '' \
--quick-add-key ${FINGERPRINT} rsa4096 encr never
gpg --export-secret-key --armor omni@internal.local > omni.asc
Step 6: Set up an identity provider
Omni requires an OIDC or SAML identity provider for user authentication. This guide uses Dex, configured with a static user. Dex can also act as a proxy to upstream providers such as LDAP, SAML, GitHub, and Okta, see the Dex connector documentation for details.
If you already have an existing SAML or OIDC provider and want to connect it directly to Omni, skip this step and see Authentication and Authorization.
6.1: Create a password hash for the admin user
You will be prompted to enter a password. This becomes your Omni login password. The hash is stored in the Dex configuration and never transmitted in plain text.
export OMNI_USER_PASSWORD=$(docker run --rm httpd:2.4-alpine \
htpasswd -BnC 15 admin | cut -d: -f2)
6.2: Write the Dex configuration
Create a dex.yaml configuration file, which configures Dex to serve OIDC over TLS on port 5556, registers Omni as an OAuth2 client, and creates a single static login user.
cat <<EOF > dex.yaml
issuer: https://${AUTH_ENDPOINT}:5556
storage:
type: memory
web:
https: 0.0.0.0:5556
tlsCert: /etc/dex/tls/server-chain.pem
tlsKey: /etc/dex/tls/server-key.pem
enablePasswordDB: true
staticClients:
- name: Omni
id: omni
secret: omni-dex-secret
redirectURIs:
- https://${OMNI_ENDPOINT}/oidc/consume
staticPasswords:
- email: "${OMNI_USER_EMAIL}"
username: "admin"
preferredUsername: "admin"
hash: "${OMNI_USER_PASSWORD}"
EOF
6.3: Run Dex
Start Dex as a container, mounting the configuration file and TLS certificates. The :Z flag on volume mounts ensures compatibility with SELinux if it is enabled on your host.
docker run -d \
--name dex \
--restart=unless-stopped \
-p 5556:5556 \
-v $(pwd)/dex.yaml:/etc/dex/dex.yaml:ro,Z \
-v $(pwd)/server-key.pem:/etc/dex/tls/server-key.pem:ro,Z \
-v $(pwd)/server-chain.pem:/etc/dex/tls/server-chain.pem:ro,Z \
ghcr.io/dexidp/dex:v2.41.1 \
dex serve /etc/dex/dex.yaml
Step 7: Run Omni
In this step you will start the Omni container and connect it to the services you set up in the previous steps: the TLS certificates from Step 4, the encryption key from Step 5, and the identity provider from Step 6.
7.1: Get the latest Omni version
Fetch the latest Omni release tag from GitHub and store it in OMNI_VERSION:
export OMNI_VERSION=$(curl -sI https://github.com/siderolabs/omni/releases/latest \
| grep -i location | awk -F '/' '{print $NF}' | tr -d '\r')
echo "Using Omni version: $OMNI_VERSION"
7.2: Create the SQLite storage directory
Omni uses SQLite alongside etcd for certain internal state.
This command creates the directory on the host that will be mounted into the container:
7.3: Start Omni
Choose the etcd mode that matches your setup.
Embedded etcd is suitable for a single Omni instance. External etcd is required for high availability or if you need to manage the datastore independently.
Embedded etcd (default)
External etcd
docker run -d \
--name omni \
--net=host \
--cap-add=NET_ADMIN \
--device /dev/net/tun:/dev/net/tun \
--restart=unless-stopped \
-v $(pwd)/ca.pem:/etc/ssl/certs/ca-certificates.crt:ro,Z \
-v $(pwd)/server-key.pem:/server-key.pem:ro,Z \
-v $(pwd)/server-chain.pem:/server-chain.pem:ro,Z \
-v $(pwd)/omni.asc:/omni.asc:ro,Z \
-v $HOME/sqlite:/_out/sqlite:rw,Z \
ghcr.io/siderolabs/omni:${OMNI_VERSION} \
--name=omni \
--cert=/server-chain.pem \
--key=/server-key.pem \
--machine-api-cert=/server-chain.pem \
--machine-api-key=/server-key.pem \
--machine-api-bind-addr=0.0.0.0:8090 \
--private-key-source=file:///omni.asc \
--event-sink-port=8091 \
--bind-addr=0.0.0.0:443 \
--k8s-proxy-bind-addr=0.0.0.0:8100 \
--advertised-api-url=https://${OMNI_ENDPOINT}/ \
--siderolink-api-advertised-url=https://${OMNI_ENDPOINT}:8090/ \
--siderolink-wireguard-advertised-addr=${HOST_PUBLIC_IP}:50180 \
--advertised-kubernetes-proxy-url=https://${OMNI_ENDPOINT}:8100/ \
--auth-auth0-enabled=false \
--auth-oidc-enabled=true \
--auth-oidc-provider-url=https://${AUTH_ENDPOINT}:5556 \
--auth-oidc-client-id=omni \
--auth-oidc-client-secret=omni-dex-secret \
--auth-oidc-scopes=openid \
--auth-oidc-scopes=profile \
--auth-oidc-scopes=email \
--sqlite-storage-path=/_out/sqlite/omni.db \
--initial-users=${OMNI_USER_EMAIL}
Use this if you have a completed external etcd cluster. See Run an etcd Cluster On-Prem to set one up first.Set the private IPs of your etcd nodes.export ETCD1_IP=<etcd-node-1-private-ip>
export ETCD2_IP=<etcd-node-2-private-ip>
export ETCD3_IP=<etcd-node-3-private-ip>
Start Omni with the external etcd flags. The --etcd-embedded=false flag disables the internal etcd instance. The --etcd-endpoints flag lists all three nodes so Omni can fail over automatically if one goes down:docker run -d \
--name omni \
--net=host \
--cap-add=NET_ADMIN \
--device /dev/net/tun:/dev/net/tun \
--restart=unless-stopped \
-v $(pwd)/ca.pem:/etc/ssl/certs/ca-certificates.crt:ro,Z \
-v $(pwd)/server-key.pem:/server-key.pem:ro,Z \
-v $(pwd)/server-chain.pem:/server-chain.pem:ro,Z \
-v $(pwd)/omni.asc:/omni.asc:ro,Z \
-v $HOME/etcd-certs/ca.pem:/etcd/ca.crt:ro,Z \
-v $HOME/etcd-certs/client.pem:/etcd/client.crt:ro,Z \
-v $HOME/etcd-certs/client-key.pem:/etcd/client.key:ro,Z \
-v $HOME/sqlite:/_out/sqlite:rw,Z \
ghcr.io/siderolabs/omni:${OMNI_VERSION} \
--name=omni \
--cert=/server-chain.pem \
--key=/server-key.pem \
--machine-api-cert=/server-chain.pem \
--machine-api-key=/server-key.pem \
--machine-api-bind-addr=0.0.0.0:8090 \
--private-key-source=file:///omni.asc \
--event-sink-port=8091 \
--bind-addr=0.0.0.0:443 \
--k8s-proxy-bind-addr=0.0.0.0:8100 \
--advertised-api-url=https://${OMNI_ENDPOINT}/ \
--siderolink-api-advertised-url=https://${OMNI_ENDPOINT}:8090/ \
--siderolink-wireguard-advertised-addr=${HOST_PUBLIC_IP}:50180 \
--advertised-kubernetes-proxy-url=https://${OMNI_ENDPOINT}:8100/ \
--etcd-embedded=false \
--etcd-endpoints=https://${ETCD1_IP}:2379,https://${ETCD2_IP}:2379,https://${ETCD3_IP}:2379 \
--auth-auth0-enabled=false \
--auth-oidc-enabled=true \
--auth-oidc-provider-url=https://${AUTH_ENDPOINT}:5556 \
--auth-oidc-client-id=omni \
--auth-oidc-client-secret=omni-dex-secret \
--auth-oidc-scopes=openid \
--auth-oidc-scopes=profile \
--auth-oidc-scopes=email \
--sqlite-storage-path=/_out/sqlite/omni.db \
--initial-users=${OMNI_USER_EMAIL}
Step 8: Verify services are running
Check that both Omni and Dex containers started successfully:
docker ps --format "table {{.Names}}\t{{.Status}}"
Expected output:
NAMES STATUS
omni Up X minutes
dex Up X minutes
Confirm Omni is serving on port 443:
curl -k https://127.0.0.1:443
This should return the Omni HTML page.
Step 9: Access the Omni UI
Omni is running on your host, but your local machine does not yet trust the CA certificate or know how to resolve the internal hostnames. The steps below set that up.
9.1: Copy the CA certificate to your local machine
Copy ca.pem from the host to your local machine. You will install it as a trusted root in the next step.
Replace <HOST_PUBLIC_IP> with your instance’s public IP and ~/path/to/your-key.pem with your SSH key path.scp -i ~/path/to/your-key.pem ubuntu@<HOST_PUBLIC_IP>:~/ca.pem ~/Downloads/omni-ca.pem
Replace <instance-name> and <your-zone> with your GCP instance name and zone.gcloud compute scp <instance-name>:~/ca.pem ~/Downloads/omni-ca.pem --zone=<your-zone>
Replace <HOST_PUBLIC_IP> with your host’s IP.scp ubuntu@<HOST_PUBLIC_IP>:~/ca.pem ~/Downloads/omni-ca.pem
9.2: Trust the CA certificate
Add the CA to your local machine’s system trust store. This tells your browser and OS to trust TLS certificates signed by this CA, including the Omni and Dex endpoints.
Linux (RHEL)
Linux (Ubuntu)
macOS
Windows
On Red Hat and Fedora based distros you can copy the ca.pem file into the /etc/pki/ca-trust/source/anchors/ folder and then run the following command to generate the trusted root store:sudo cp ca.pem /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
On Ubuntu and Debian based Linux distros you can copy the ca.pem file into the /usr/local/share/ca-certificates/ directory and rename it to ca.crtsudo cp ca.pem /usr/local/share/ca-certificates/ca.crt
sudo update-ca-certificates
For macOS you should open the Keychain Access application and drag the ca.pem file into the window to install it.
On Windows you should rename the certificate extension from .pem to .crt. You can then double click on the file and select Install Certificate -> Local Machine. You then need to select “Place all certificates in the following store” and select the Trusted Root Certification Authorities.If you’re using Windows Subsystem for Linux (WSL) you should follow the Linux guide for installing the certificate.
9.3: Add internal hostnames to your local hosts file
Map the Omni and Dex hostnames to your host’s public IP so your local machine can resolve them. Replace <HOST_PUBLIC_IP> with your host’s public IP.
sudo sh -c 'echo "<HOST_PUBLIC_IP> omni.internal auth.internal" >> /etc/hosts'
Add the following line to C:\Windows\System32\drivers\etc\hosts:<HOST_PUBLIC_IP> omni.internal auth.internal
9.5: Log into Omni
Open https://omni.internal in your browser and sign in with the email and password you set in Step 6.2.