Files
vantage-docs/docs/getting-started/self-hosted-install.md
T
2026-09-10 08:43:43 +00:00

9.2 KiB

id, title, sidebar_label
id title sidebar_label
self-hosted-install Install Vantage (self-hosted) Self-hosted install

This puts the control plane on a host you own. Budget about fifteen minutes.

Before you start

You need:

  • A Linux host with Docker and the Compose plugin.
  • A DNS name pointing at that host. People use it for the web UI, and your agents use it too.
  • A way to terminate TLS. The stack ships an nginx reverse proxy that routes the web UI and API on plain HTTP; you can terminate TLS in that nginx (see step 4) or in a proxy you already run in front of it. The agent port, 9090, speaks HTTP/2.
  • Those two ports reachable: the web port from wherever your people are, and 9090 from every machine you intend to manage.
  • Outbound access from the control plane, and from each managed machine, to gitea.hostxtra.co.uk, which serves the agent downloads.

The stack brings MongoDB, Redis and the console daemon with it, so there is no database to provide.

1. Get the Compose file and proxy config

mkdir -p /opt/vantage/nginx && cd /opt/vantage
curl -fsSLO https://gitea.hostxtra.co.uk/vantage/vantage-app/raw/branch/main/deploy/docker/docker-compose.yml
curl -fsSL -o nginx/vantage.conf https://gitea.hostxtra.co.uk/vantage/vantage-app/raw/branch/main/deploy/docker/nginx/vantage.conf

The Compose file mounts ./nginx/vantage.conf into the nginx service, so it must sit at that path next to docker-compose.yml.

2. Write the environment file

Create /opt/vantage/.env:

# The host:port your agents connect to. This is not the web URL;
# this port speaks gRPC.
GRPC_HOST=vantage.example.com:9090

# 32 bytes as 64 hex characters. Generate it with the command below.
KEY_ENCRYPTION_KEY=

# The host serving agent downloads.
GITEA_HOST=gitea.hostxtra.co.uk

# Optional: host port for the bundled nginx proxy. Defaults to 80.
# NGINX_HTTP_PORT=80

Generate the encryption key:

openssl rand -hex 32

Then make sure the server service passes GITEA_HOST through, by adding this line to its environment: block in docker-compose.yml:

GITEA_HOST: ${GITEA_HOST}

Without it, the install command you hand to a new server cannot work out which agent to download.

:::danger Keep the encryption key safe KEY_ENCRYPTION_KEY encrypts SSH private keys, vault secrets, single sign-on client secrets and console credentials. If you lose it, all of those become unreadable and there is no way to recover them. Back it up somewhere other than the server it protects, and do not change it once the install is in use. :::

:::warning GRPC_HOST has no default The server will not start without it. There is deliberately no fallback to your web address, because that port does not speak the protocol agents use, and the mistake would only show up later as every agent failing to connect. :::

3. Start the stack

docker compose up -d
docker compose ps

Six services start: mongo, redis, guacd, server, web and nginx.

Check the server got through startup:

docker compose logs -f server

On first boot it prepares the database and loads the built-in workflow step library. If it stops during that, it will say why, and it is meant to stop rather than run in a half-prepared state.

4. The reverse proxy

The nginx service routes one hostname to two backends:

Path Backend
/api, /auth, /public, /install, /install.ps1, /update, /update.ps1 server:8080
everything else web:3000

Both rules are required. The web app forwards nothing to the API, so a proxy that sends the whole hostname to web:3000 serves the interface and answers 404 to every request it makes — starting with the login form. If you replace the bundled nginx with your own proxy, it must route the same way.

This is the shipped nginx/vantage.conf:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

upstream vantage_server {
    server server:8080;
    keepalive 16;
}

upstream vantage_web {
    server web:3000;
    keepalive 16;
}

server {
    listen 80;
    listen [::]:80;
    server_name _;

    client_max_body_size 10m;

    proxy_http_version 1.1;
    proxy_set_header Host              $host;
    proxy_set_header X-Forwarded-Host  $host;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Upgrade           $http_upgrade;
    proxy_set_header Connection        $connection_upgrade;

    # REST API. The browser console is a WebSocket and workflow logs stream,
    # so no buffering and a long read timeout.
    location ^~ /api/ {
        proxy_pass http://vantage_server;
        proxy_buffering off;
        proxy_read_timeout 1h;
        proxy_send_timeout 1h;
    }

    location ^~ /auth/ {
        proxy_pass http://vantage_server;
    }

    location ^~ /public/ {
        proxy_pass http://vantage_server;
    }

    # /install, /install.ps1, /update, /update.ps1
    location ~ ^/(install|update)(\.ps1)?$ {
        proxy_pass http://vantage_server;
    }

    location / {
        proxy_pass http://vantage_web;
    }
}

The Upgrade/Connection headers and proxy_buffering off are not optional: without them the browser console cannot open its WebSocket and live workflow logs arrive in bursts or not at all.

Terminating TLS in nginx

The shipped config speaks plain HTTP, which is right when another proxy or load balancer in front of it already terminates TLS. To terminate TLS in the bundled nginx instead, mount your certificate and add a 443 server, redirecting 80 to it.

In docker-compose.yml, extend the nginx service:

nginx:
    ports:
        - 80:80
        - 443:443
    volumes:
        - ./nginx/vantage.conf:/etc/nginx/conf.d/default.conf:ro
        - ./certs:/etc/nginx/certs:ro

In nginx/vantage.conf, replace the listen 80 lines of the existing server with the TLS ones, and add a redirect server:

server {
    listen 80;
    listen [::]:80;
    server_name vantage.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name vantage.example.com;

    ssl_certificate     /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;

    # ... the rest of the shipped server block, unchanged ...
}

Agent traffic on 9090

Agents connect to port 9090. Vantage does not terminate TLS itself, so put that port behind your proxy too, with a certificate valid for the name in GRPC_HOST. The proxy must speak HTTP/2 through to Vantage. Many do not do so by default, and the symptom is agents that register once and then stop responding.

With the bundled nginx, remove 9090:9090 from the server service's ports, add 9090:9090 to the nginx service, and add this server to nginx/vantage.conf:

server {
    listen 9090 ssl;
    listen [::]:9090 ssl;
    http2 on;
    server_name vantage.example.com;

    ssl_certificate     /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;

    # Command streams are long-lived; do not time them out.
    grpc_read_timeout 1h;
    grpc_send_timeout 1h;

    location / {
        grpc_pass grpc://server:9090;
    }
}

grpc:// is plain HTTP/2 (h2c) to the container, which is what the server speaks.

For a private network where TLS is not required, you can instead set tls: false in each agent's config and let agents reach the port directly.

5. First sign-in

Open your hostname in a browser. With no users in the database yet, you are sent to the setup page.

Continue with First login.

Verifying the install

Check Expected
docker compose ps six services running
curl -s localhost:8080/auth/bootstrap-status JSON saying bootstrap is needed
curl -s localhost/auth/bootstrap-status the same JSON, through nginx
nc -z your-host 9090 open
docker compose logs server no fatal errors

Common install problems

The server exits immediately. Almost always a missing GRPC_HOST. The log names it.

Agents register but never go active. They reached port 9090 once but cannot hold the connection, or your proxy is not passing HTTP/2 through. Test from the managed machine, not from the control plane host.

Secrets pages show an error. KEY_ENCRYPTION_KEY is empty or is not 64 hex characters.

More in Troubleshooting.

What is not included

The marketing site, the Vantage HQ portal and this documentation site are hosted by us. A self-hosted install runs none of them, and it never holds the key that signs licences.