Guides

HTTPS and domain

This document explains how to put a Let's Encrypt certificate on the panel with ssl-setup.sh, how renewal works and what the Cloudflare helper does.

Before you start

  • The panel is installed and all containers are healthy (xtreamgo health).
  • You have a domain or subdomain with an A record that points at the public IPv4 address of the server.
  • Port 80 is reachable from the internet. Let's Encrypt checks the domain over HTTP on port 80.
  • Port 443 is reachable from the internet.
  • You have an e-mail address for Let's Encrypt notices.
  • The script works before and after the first panel update. See the note about updated panels below.

Steps

1. Check the DNS record

bash
getent ahostsv4 panel.example.com | head -1
curl -s -4 ifconfig.me; echo

Both commands must print the same IP address.

2. Download the script into the install directory

bash
cd /home/xtreamgo
wget https://api.xtreamgo.io/install/ssl-setup.sh

The script is listed in SHA256SUMS. You can verify it the same way as the installer:

bash
wget -q https://api.xtreamgo.io/install/SHA256SUMS -O /tmp/SHA256SUMS
grep ' ssl-setup.sh$' /tmp/SHA256SUMS | sha256sum -c -

3. Run it

bash
bash ssl-setup.sh panel.example.com admin@example.com

Without arguments the script asks for Domain (e.g. panel.example.com): and E-mail for Let's Encrypt notices:.

4. Read the output

A successful run prints these lines in order:

text
[SSL] Checking that panel.example.com points to this server...
[SSL] Making sure nginx runs with the certbot volumes...
[SSL] Requesting the certificate for panel.example.com...
[SSL] Installing the certificate files...
[SSL] Writing nginx TLS configuration...
[SSL] nginx reloaded. HTTPS is active on https://panel.example.com
[SSL] CORS updated, api restarted
[SSL] Renewal cron installed (3:17 on the 1st and 15th). Done.

The line CORS updated, api restarted appears on a panel that has not received an update yet. CORS is the browser rule that lets the admin panel call the API from your domain. On an updated panel the script prints this line instead:

text
[SSL] CORS origin saved. The panel runs in blue/green mode, so it is applied at the next panel update (starting the old api service here would run two APIs).

Blue/green mode means that the panel keeps the previous API version stopped next to the new one so it can switch back. Every updated panel runs in this mode.

Neither line appears when https://<domain> is already part of CORS_ORIGINS.

What the script changes

ChangeDetail
CertificateRequested with the certbot/certbot:v2.11.0 image and the webroot method. Stored in the Docker volume that nginx mounts at /etc/letsencrypt.
/home/xtreamgo/ssl/cert.pem, /home/xtreamgo/ssl/key.pemCopies of the full chain and the private key. nginx reads these two files.
/home/xtreamgo/nginx/conf.d/ssl.confA server block on port 443 for your domain. It forwards every request to the existing port 80 block, so all panel, API and stream paths work over HTTPS. An existing file is kept as ssl.conf.bak-<timestamp>.
nginxConfiguration test, then reload. If the test fails the previous state is restored.
/home/xtreamgo/.env.productionSets DOMAIN and SSL_EMAIL. Adds https://<domain> to CORS_ORIGINS.
API containerBefore the first panel update: recreated with docker compose up -d api, so that the new CORS origin applies at once. After the first update (blue/green mode): not touched. The new origin is saved and applies at the next panel update.
/home/xtreamgo/ssl-renew.shRenewal script, generated by ssl-setup.sh. Mode 700.
root crontab17 3 1,15 * * /home/xtreamgo/ssl-renew.sh >/dev/null 2>&1 # xtreamgo-ssl-renew

What the script does not change:

  • It does not redirect HTTP to HTTPS. Port 80 keeps serving the panel.
  • It does not change the URLs inside M3U playlists. The playlist output always writes stream URLs that start with http://.
  • It does not update CREDENTIALS.txt.
  • It issues a certificate for one host name. It cannot issue a wildcard certificate.

Renewal

ssl-setup.sh generates the script /home/xtreamgo/ssl-renew.sh and adds a cron entry for it. The cron entry runs the script at 03:17 on the 1st and the 15th of each month.

The renewal script:

  1. Runs certbot renew with the certbot/certbot:v2.11.0 image.
  2. Copies the full chain and the private key into temporary files inside /home/xtreamgo/ssl.
  3. Checks that the temporary files contain a certificate and a private key.
  4. Moves the temporary files over cert.pem and key.pem. Each file is replaced in one step, so nginx never reads a half written file.
  5. Reloads nginx.

A failed step ends the script with exit code 1 and leaves the live files untouched.

Run ssl-setup.sh again when you change the domain. The renewal script holds the domain it was generated for.

Check the cron entry and run a renewal by hand:

bash
crontab -l | grep xtreamgo-ssl-renew
bash /home/xtreamgo/ssl-renew.sh; echo "exit code: $?"

Exit code 0 means the script finished. Certbot only renews a certificate that is close to its expiry date. The cron entry discards the output of the script, so a failed renewal is not reported. Check the expiry date from time to time with the commands in "How to check it worked".

Renewal runs through the cron entry above only. No certbot container keeps running between renewals.

Note for panels that have already received an update

After the first update the API runs in a container named xtreamgo-api-blue or xtreamgo-api-green that Docker Compose does not manage. ssl-setup.sh detects this.

  • It starts nginx with docker compose up -d --no-deps nginx. The option --no-deps keeps Docker Compose from starting the original xtreamgo-api container.
  • It does not run docker compose up -d api. It saves the new CORS origin in .env.production and prints the CORS origin saved line shown above.
  • The saved origin reaches the API at the next panel update. The update builds the settings of the new API container from .env.production.

Until that update the running API keeps its previous CORS_ORIGINS value. docker restart does not apply the new value, because a restarted container keeps the settings it was created with. If the admin panel does not work over https://<domain> before the next update, contact XtreamGo support.

You can confirm that only one API container runs:

bash
docker ps --filter name=xtreamgo-api --format '{{.Names}}\t{{.Status}}'

If the list shows xtreamgo-api and also a container with the suffix -blue or -green, follow item 15 of Troubleshooting.

Cloudflare helper

setup-cloudflare.sh is an optional script for operators who use Cloudflare DNS. The installer offers it with the question Configure Cloudflare now? (y/n) [n]:. You can run it later:

bash
cd /home/xtreamgo
wget https://api.xtreamgo.io/install/setup-cloudflare.sh
bash setup-cloudflare.sh

It asks for a Cloudflare API token, the zone and the panel IP. The token needs the permissions Zone.DNS (Edit) and Zone.Zone (Read) for your zone. Flags: --token=, --zone=, --panel-ip=, --no-https-redirect, --no-proxy, --install-dir=.

What it does:

  1. Verifies the token.
  2. Finds the zone ID.
  3. Creates or updates the wildcard record *.<zone> as an A record to the panel IP, with the Cloudflare proxy on.
  4. Sets always_use_https to on, the SSL mode to full and automatic_https_rewrites to on. Skip this with --no-https-redirect.
  5. Writes the zone into dns_settings.public_base_domain in the panel database.

Read these points before you use it:

  • SSL mode full makes Cloudflare connect to your server over HTTPS on port 443. The panel only answers on port 443 after a certificate is installed. ssl-setup.sh issues a certificate for one host name and writes a server block for that name only. The installer and ssl-setup.sh do not issue a certificate for the wildcard name. Without a matching setup on port 443, requests through Cloudflare fail.
  • When you answer no to the installer question, the installer prints Skipping Cloudflare setup. Run later: bash /home/xtreamgo/setup-cloudflare.sh. Download the script with the command above before you run it.
  • The script needs python3 and curl on the server.

How to check it worked

bash
curl -sI https://panel.example.com/health | head -1
echo | openssl s_client -connect panel.example.com:443 -servername panel.example.com 2>/dev/null | openssl x509 -noout -subject -dates
docker exec xtreamgo-nginx nginx -t
ls -l /home/xtreamgo/ssl/

Expected results: HTTP/2 200, a subject with your domain, a notAfter date about 90 days ahead, syntax is ok and test is successful, and both cert.pem and key.pem with a size above zero.

If something goes wrong

MessageMeaningAction
[ERROR] docker-compose.yml not found in ... or .env.production not found in ...The script cannot find the install directory.Run it from /home/xtreamgo.
[ERROR] Invalid domain: ... or Invalid e-mail: ...The argument has the wrong format.Pass a host name such as panel.example.com and a full e-mail address.
[WARN] <domain> resolves to <ip> but this server is <ip>. Let's Encrypt will fail unless the A record points here.The A record points elsewhere, or a proxy sits in front of the server.Fix the A record. Wait for DNS to update. If you use a DNS proxy, switch it to DNS only for the request.
[ERROR] nginx has no /etc/letsencrypt and /var/www/certbot mounts.The compose file is older than the package version that added the certificate volumes.Contact XtreamGo support for the current docker-compose.yml.
Certbot error and the script stops after Requesting the certificateLet's Encrypt could not reach http://<domain>/.well-known/acme-challenge/, or a rate limit applies.Check port 80 from outside, the A record and the provider firewall. Read the certbot text for the exact reason.
[ERROR] certificate files are emptyThe copy from the certificate volume failed.Run the script again.
[ERROR] nginx rejected the TLS configuration; previous state restorednginx -t failed with the new ssl.conf. The last three lines of the test are printed above the message.Send these lines to XtreamGo support. HTTP keeps working.
Browser shows an expired certificateRenewal did not run or failed.Run bash /home/xtreamgo/ssl-renew.sh; echo $?. Check crontab -l. Check that port 80 is still reachable.
Cloudflare helper: Token verification failed. Check the token and permissions.The token is wrong or lacks permissions.Create a new token with Zone.DNS (Edit) and Zone.Zone (Read).
Cloudflare helper: Could not find a Cloudflare zone matching ...The zone is not in the account, or the token does not include it.Add the zone or widen the token.

Back to documentation