Skip to content

HTTPS and certificates

Gamepads, Keyboard Lock and the microphone need HTTPS. Quasar makes and keeps its own certificate on first boot, so you don’t need one.

On an owned install, the certificate’s names come from two seed inputs, read at the first install:

QUASAR_PUBLIC_HOST: "192.0.2.50"
QUASAR_TLS_HOSTS: "quasar.lan,quasar.vpn.example"

List every address anyone types into a browser: LAN IP, hostname, VPN address. Changing them later means re-issuing the certificate (below). On a legacy Compose install, set QUASAR_TLS_HOSTS in deploy/.env.

Install the certificate in each device’s trust store instead of accepting the browser warning every time. Download it (no login needed; it’s public):

Terminal window
curl -k https://<host-ip>:8443/v1/tls/certificate.pem -o quasar.pem

Add quasar.pem to your platform’s trust store (macOS: Keychain Access, Always Trust; Windows: Trusted Root Certification Authorities; Linux: usually /usr/local/share/ca-certificates then update-ca-certificates).

On a network you don’t control, compare the fingerprint with what the browser shows before the first visit, when the first admin is claimed:

Terminal window
docker logs quasar-control-plane 2>&1 | grep fingerprint

(Legacy Compose: docker compose -f deploy/docker-compose.yml logs quasar-control-plane.)

Owned install: change the names with reconfigure, then re-issue the certificate:

Terminal window
docker exec -it quasar-recovery quasar-recovery reconfigure --yes \
QUASAR_TLS_HOSTS=quasar.lan,quasar.vpn.example,192.0.2.50
docker exec quasar-control-plane rm -f /var/lib/quasar-control/tls/cert.pem /var/lib/quasar-control/tls/key.pem
docker restart quasar-control-plane

The new fingerprint means browsers warn again, and separately added GPU hosts must be added again from Admin ▸ Fleet ▸ Add host (a combined host’s own agent is fine). To avoid this, use a name the certificate already has, or a reverse proxy.

Legacy Compose:

  1. Add the name to QUASAR_TLS_HOSTS in deploy/.env.

  2. Delete the existing pair.

    Terminal window
    docker compose -f deploy/docker-compose.yml exec quasar-control-plane \
    rm -f /var/lib/quasar-control/tls/cert.pem /var/lib/quasar-control/tls/key.pem
  3. Recreate the container.

    Terminal window
    docker compose -f deploy/docker-compose.yml up -d --force-recreate quasar-control-plane

Every browser and trust store needs the new certificate.

Owned install: not supported in this release. Put a reverse proxy with your certificate in front.

Legacy Compose: mount both PEM files and set both (one alone stops startup):

Terminal window
QUASAR_TLS_CERT=/etc/quasar/tls/cert.pem
QUASAR_TLS_KEY=/etc/quasar/tls/key.pem

The certificate needs:

  • Every browser-facing name as a subject alternative name (IPs as IP SANs).
  • The full chain, leaf first.
  • An unencrypted private key.
  • Extended key usage including server authentication.

The proxy’s certificate is the one browsers see. Check allowed origins under Admin ▸ Settings. See Behind a reverse proxy.

Legacy Compose only: QUASAR_TLS=off in deploy/.env. You lose gamepads, Escape capture and the microphone unless something in front provides HTTPS. An owned install always serves HTTPS; a proxy forwards to its HTTP port.

With TLS off, first-run setup and sign-in work only through a proxy named in QUASAR_TRUSTED_PROXIES that sends X-Forwarded-Proto: https. Without one, nobody can sign in.

Off by default. QUASAR_HSTS_MAX_AGE (seconds) makes the HTTPS listener send Strict-Transport-Security: max-age=<n>; the HTTP port never does, and no subdomains are named. On an owned install, on the first machine:

Terminal window
docker exec -it quasar-recovery quasar-recovery reconfigure --yes QUASAR_HSTS_MAX_AGE=86400

An empty value turns it off again (a browser that already saw it keeps it until the max-age runs out). It needs a release whose control-plane image is recipe revision 4 or later; on an older one the command stops and says so.

HSTS pins the whole host name, on every port. Once a browser trusts the certificate and has seen the header, any other http:// service on that name (a NAS page, this control plane’s own HTTP port) is forced to HTTPS until the max-age ends. Start with a short value such as 86400. IP addresses are never pinned, and a browser that does not trust the certificate ignores the header.

It worked when curl -skI https://<console>:8443/health shows the header and the same request to the HTTP port does not.

Owned install (seed inputs):

Seed input Default Purpose
QUASAR_PUBLIC_HOST unset The name or address of the console. The certificate claims it.
QUASAR_TLS_HOSTS unset More names and IPs for the certificate, comma-separated.
QUASAR_HSTS_MAX_AGE unset (off) Seconds of HSTS on the HTTPS listener. See HSTS.
QUASAR_TLS_PORT 8443 Host port for HTTPS. Browser routes on the HTTP port redirect here.
QUASAR_HTTP_PORT 8080 Host port for HTTP.

Legacy Compose (deploy/.env):

Setting Default Purpose
QUASAR_TLS auto auto makes and keeps a self-signed pair. off disables HTTPS.
QUASAR_TLS_HOSTS unset Names and IPs the generated certificate claims.
QUASAR_TLS_CERT / QUASAR_TLS_KEY unset Your own PEM files. Set both.
QUASAR_TLS_ADDR :8443 The HTTPS listener.
LISTEN_ADDR :8080 The HTTP listener.
QUASAR_HTTP_REDIRECT auto Redirects browser routes from HTTP to HTTPS.
QUASAR_TLS_REDIRECT_PORT the TLS port The external port to redirect to, if different.