> ## Documentation Index
> Fetch the complete documentation index at: https://docs.siderolabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Expose Omni with Nginx (HTTPS)

> Configure Nginx as an HTTPS reverse proxy for on-premises Omni deployments.

This guide assumes Omni is deployed following the [Run Omni On-Prem](./run-omni-on-prem) guide. However, the flags used in the **Deploy Omni** section are modified as shown in **Step 1** so that Nginx handles all public traffic.

In this setup, Nginx runs in front of Omni and routes traffic to three public endpoints:

| Endpoint                         | Purpose                |
| -------------------------------- | ---------------------- |
| `https://$OMNI_DOMAIN_NAME`      | Omni UI and main API   |
| `https://api.$OMNI_DOMAIN_NAME`  | SideroLink machine API |
| `https://kube.$OMNI_DOMAIN_NAME` | Kubernetes proxy       |

Omni itself listens only on localhost, while Nginx handles TLS termination and all external traffic.

## Prerequisites

Before proceeding, ensure the following:

* Ensure Docker is installed on the machine serving public traffic
* Open ports 80 and 443 on the Nginx machine
* Create the following DNS A records and point them to the public IP address of the Nginx machine:
  * `<omni-domain-name>`
  * `api.<omni-domain-name>`
  * `kube.<omni-domain-name>`

Replace `<omni-domain-name>` with your Omni domain name (for example, `omni.example.com`) in the DNS records above and throughout this guide.

## Step 1: Start Omni with the Nginx Flags

Follow the [Run Omni On-Prem](./run-omni-on-prem) guide to deploy Omni on-prem.\
Before starting the Omni instance using either `docker run` or `docker compose`, complete the following steps.

1. Verify that the required environment variables from the on-prem guide are set in your shell:

   ```bash theme={null}
   echo $OMNI_DOMAIN_NAME
   echo $OMNI_ACCOUNT_UUID
   ```

2. If any variables are missing, export them now:

   ```bash theme={null}
   export OMNI_DOMAIN_NAME=<omni-domain>     # e.g omni.example.com
   export OMNI_ACCOUNT_UUID=<account-id>      # your Omni account ID
   ```

3. Start Omni using the following flags. Remember to include the authentication flags required by your authentication provider. For more details, see the [configure authentication guide](./run-omni-on-prem#configure-authentication):

   ```bash theme={null}
   --name=$OMNI_NAME \
   --private-key-source=file:///omni.asc \
   --bind-addr=127.0.0.1:8080 \
   --advertised-api-url=https://$OMNI_DOMAIN_NAME/ \
   --siderolink-api-bind-addr=127.0.0.1:8090 \
   --siderolink-api-advertised-url=https://api.$OMNI_DOMAIN_NAME:443 \
   --k8s-proxy-bind-addr=127.0.0.1:8100 \
   --advertised-kubernetes-proxy-url=https://kube.$OMNI_DOMAIN_NAME/ \
   --account-id=$OMNI_ACCOUNT_UUID
   # Also add the authentication flags according to your setup
   ```

   <Info>Authentication flags are not included here. Add the appropriate flags for your auth provider as described in the [authentication configuration guide](./run-omni-on-prem#configure-authentication). </Info>

   **Example:** Starting an Omni instance might look similar to the following:

   ```bash theme={null}
   docker run \
       --net=host \
       --cap-add=NET_ADMIN \
       --device /dev/net/tun \
       -v $PWD/etcd:/_out/etcd \
       -v $PWD/omni.asc:/omni.asc \
       ghcr.io/siderolabs/omni:<tag> \
           --name=$OMNI_NAME \
           --private-key-source=file:///omni.asc \
           --bind-addr=127.0.0.1:8080 \
           --advertised-api-url=https://$OMNI_DOMAIN_NAME/ \
           --siderolink-api-bind-addr=127.0.0.1:8090 \
           --siderolink-api-advertised-url=https://api.$OMNI_DOMAIN_NAME:443 \
           --k8s-proxy-bind-addr=127.0.0.1:8100 \
           --advertised-kubernetes-proxy-url=https://kube.$OMNI_DOMAIN_NAME/ \
           --account-id=$OMNI_ACCOUNT_UUID \
           --auth-auth0-enabled=true \
           --auth-auth0-domain=<Auth0 domain> \
           --auth-auth0-client-id=<Auth0 client ID> \
           --initial-users=<email address>
   ```

## Step 2: Provision TLS Certificates

Use `acme` or `certbot` to generate TLS certificates for all three subdomains. **Step 3** expects the certificates at the following paths. If your certificates are stored elsewhere, update the paths in the Nginx config accordingly.

| Subdomain                | Certificate path           |
| ------------------------ | -------------------------- |
| `$OMNI_DOMAIN_NAME`      | `/var/lib/acme/omni/`      |
| `api.$OMNI_DOMAIN_NAME`  | `/var/lib/acme/omni_api/`  |
| `kube.$OMNI_DOMAIN_NAME` | `/var/lib/acme/omni_kube/` |

## Step 3: Apply the Nginx Configuration

Run the following command to write the Nginx config:

```bash theme={null}
cat <<EOF > /etc/nginx/nginx.conf
http {
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_connect_timeout 60s;
    # Omni requires long timeouts for long-lived connections
    proxy_send_timeout 1h;
    proxy_read_timeout 1h;

    # Used for WebSocket proxying
    map \$http_upgrade \$connection_upgrade {
        default upgrade;
        '' close;
    }

    # Redirect HTTP to HTTPS
    server {
        listen 0.0.0.0:80;
        listen [::0]:80;
        server_name $OMNI_DOMAIN_NAME;
        location / {
            return 301 https://\$host\$request_uri;
        }
    }

    map \$http_content_type \$is_grpc {
        default 0;
        "application/grpc" 1;
    }

    # Main Omni API — routes gRPC and HTTP traffic
    server {
        listen 0.0.0.0:443 http2 ssl;
        listen [::0]:443 http2 ssl;
        server_name $OMNI_DOMAIN_NAME;
        ssl_certificate /var/lib/acme/omni/fullchain.pem;
        ssl_certificate_key /var/lib/acme/omni/key.pem;
        ssl_trusted_certificate /var/lib/acme/omni/chain.pem;

        location / {
            error_page 418 = @grpc;
            error_page 419 = @http;
            if (\$is_grpc) {
                return 418;
            }
            return 419;
        }

        location @grpc {
            # Long timeouts required for long-lived gRPC stream connections
            grpc_read_timeout 1h;
            grpc_send_timeout 1h;
            grpc_pass grpc://127.0.0.1:8080;
        }

        location @http {
            proxy_pass http://127.0.0.1:8080;
            proxy_set_header Upgrade \$http_upgrade;
            proxy_set_header Connection \$connection_upgrade;
        }
    }

    # SideroLink (Machine) API
    server {
        listen 0.0.0.0:443 http2 ssl;
        listen [::0]:443 http2 ssl;
        server_name api.$OMNI_DOMAIN_NAME;
        ssl_certificate /var/lib/acme/omni_api/fullchain.pem;
        ssl_certificate_key /var/lib/acme/omni_api/key.pem;
        ssl_trusted_certificate /var/lib/acme/omni_api/chain.pem;

        location / {
            # Long timeouts required for long-lived gRPC stream connections
            grpc_read_timeout 1h;
            grpc_send_timeout 1h;
            grpc_pass grpc://127.0.0.1:8090;
        }
    }

    # Kubernetes Proxy API
    server {
        listen 0.0.0.0:443 http2 ssl;
        listen [::0]:443 http2 ssl;
        server_name kube.$OMNI_DOMAIN_NAME;
        ssl_certificate /var/lib/acme/omni_kube/fullchain.pem;
        ssl_certificate_key /var/lib/acme/omni_kube/key.pem;
        ssl_trusted_certificate /var/lib/acme/omni_kube/chain.pem;

        location / {
            proxy_pass http://127.0.0.1:8100;
            proxy_set_header Upgrade \$http_upgrade;
            proxy_set_header Connection \$connection_upgrade;
        }
    }
}
EOF
```

Start the Nginx container with the config and certificates mounted:

```bash theme={null}
docker run -d \
  --name nginx \
  --net=host \
  --restart unless-stopped \
  -v /etc/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
  -v /var/lib/acme:/var/lib/acme:ro \
  nginx
```

<Info>The `--net=host` flag is required so that Nginx can reach Omni over `127.0.0.1`. If Nginx and Omni run on separate machines, replace `127.0.0.1` in the config with the internal IP address of the Omni machine before starting the container.</Info>

If you update the Nginx configuration later and need to apply the changes without restarting the container, run:

```bash theme={null}
docker exec nginx nginx -s reload
```

## Endpoints

Once Nginx is running, your Omni instance is accessible at:

| Service                  | URL                               |
| ------------------------ | --------------------------------- |
| Omni UI & API            | `https://$OMNI_DOMAIN_NAME/`      |
| SideroLink (Machine) API | `https://api.$OMNI_DOMAIN_NAME/`  |
| Kubernetes Proxy         | `https://kube.$OMNI_DOMAIN_NAME/` |
