Install the portal
Unpack the server release, set up PostgreSQL, the portal's account, folders and secrets, create its database and run it as a systemd service.
- For
- Administrators
- Applies to
- Billet 1.0
- Updated
These steps are for Ubuntu 24.04 LTS, with the portal's database in PostgreSQL. Run them as a user who can use sudo.
The portal listens on 127.0.0.1:5150 only. Nothing reaches it except through the reverse proxy you set up next, which decides which addresses are public.
Note: Use PostgreSQL for every deployment people rely on. The portal can also run on a SQLite file, but only for a lab or test deployment: SQLite cannot be shared by a second portal node, and it is not what Billet supports for real use. See SQLite, for a lab or test only.
1. Install the packages it needs
sudo apt-get update
sudo apt-get install -y nginx openssl ca-certificates libxmlsec1t64-openssl postgresql
The portal links the xmlsec1 library for SAML sign-in and will not start without it. Leave out postgresql if your database runs on a server of its own.
2. Create the database
On the PostgreSQL server, create an account and a database for the portal. Use a long random password made of letters and digits, so it needs no escaping in a connection address:
openssl rand -hex 24 # copy this, it is the database password
sudo -u postgres createuser --pwprompt billet
sudo -u postgres createdb --owner=billet billet
createuser asks for the password twice. Paste the one you just made.
If PostgreSQL is on another server, allow the portal's address to connect to the billet database in that server's pg_hba.conf, and use TLS between them.
3. Check and unpack the release
sha256sum -c SHA256SUMS
tar -xzf Billet-Server-1.0.0-linux-x64.tar.gz
sha256sum must print OK for the archive. If it does not, download it again.
4. Create the service account and folders
sudo useradd --system --no-create-home --home-dir /opt/billet/portal \
--shell /usr/sbin/nologin billet
sudo install -d -o billet -g billet -m 0755 /opt/billet/portal
sudo install -d -o billet -g billet -m 0750 /var/lib/billet
sudo install -d -o billet -g billet -m 0700 /var/lib/billet-portal
sudo install -d -o root -g billet -m 0750 /etc/billet
| Folder | Holds |
|---|---|
/opt/billet/portal |
The program, its pages and its configuration |
/var/lib/billet |
The portal's working files |
/var/lib/billet-portal |
The certificate authority and this node's install ID |
/etc/billet |
The portal's secrets |
5. Copy the portal into place
sudo cp -r Billet-Server-1.0.0-linux-x64/portal/. /opt/billet/portal/
sudo cp /opt/billet/portal/config/production.yaml.example \
/opt/billet/portal/config/production.yaml
sudo install -d /opt/billet/portal/downloads
sudo install -m 0644 Billet-Server-1.0.0-linux-x64/agent/billet-stats-agent \
/opt/billet/portal/downloads/
sudo chown -R billet:billet /opt/billet/portal
production.yaml holds settings that are not secret, and reads the database address and pool size from the environment. Secrets never go in it.
The portal hands the Linux stats agent in downloads to the one-command installer for a Linux host. Copy it again after every upgrade, so new desktops get the agent from the same release as the portal.
6. Write the secrets
Replace <database password> with the password from step 2, and localhost with the database server's name if it is elsewhere:
sudo tee /etc/billet/portal.env > /dev/null <<EOF
BILLET_SESSION_SECRET=$(openssl rand -hex 32)
BILLET_BROKER_SECRET=$(openssl rand -hex 32)
DATABASE_URL=postgres://billet:<database password>@localhost:5432/billet
DB_MAX_CONNECTIONS=10
EOF
sudo chown root:billet /etc/billet/portal.env
sudo chmod 0640 /etc/billet/portal.env
DB_MAX_CONNECTIONS sets the portal's connection pool. Its default is 1, which suits only a SQLite lab or test portal.
Warning: Generate the two secrets once and keep them, with your backups. Changing
BILLET_SESSION_SECRETsigns everybody out. ChangingBILLET_BROKER_SECRETends sessions in progress, and the portal can no longer read the license key it stored, so you would have to enter it again.
7. Add a helper for portal commands
Several later steps run the portal's own command line as the billet account with the service's secrets loaded. This short script does that, so no secret is ever typed on a command line:
sudo tee /usr/local/sbin/billet-portal-task > /dev/null <<'EOF'
#!/bin/sh
# Run billet_portal-cli as the billet account, with the portal service's
# environment. Usage: sudo billet-portal-task <arguments>
set -e
set -a
. /etc/billet/portal.env
set +a
cd /opt/billet/portal
exec runuser -u billet -- env LOCO_ENV=production \
BILLET_CA_DIR=/var/lib/billet-portal/ca ./billet_portal-cli "$@"
EOF
sudo chmod 0750 /usr/local/sbin/billet-portal-task
8. Create the tables
The portal does not change its database by itself when it starts, so that two nodes starting at once never race to alter it. Create the tables now, and run the same command after every upgrade:
sudo billet-portal-task db migrate -e production
9. Run it as a service
Create /etc/systemd/system/billet-portal.service, replacing portal.example.com with your portal's name:
[Unit]
Description=Billet portal
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
Type=simple
User=billet
Group=billet
WorkingDirectory=/opt/billet/portal
ExecStart=/opt/billet/portal/billet_portal-cli start -e production -b 127.0.0.1 -p 5150
Environment=LOCO_ENV=production
EnvironmentFile=/etc/billet/portal.env
# The name people and machines reach the portal at, without https://.
Environment=BILLET_PUBLIC_HOST=portal.example.com
# Redirect plain HTTP to HTTPS and mark sign-in cookies Secure.
Environment=BILLET_FORCE_HTTPS=1
# The deployment's own certificate authority, which the portal trusts when it
# talks to a relay. Created in a later step.
Environment=BILLET_TLS_ROOTS=/var/lib/billet-portal/ca/ca.crt
# Where the agent for Linux DCV desktops is (step 5).
Environment=BILLET_AGENT_DIST_DIR=/opt/billet/portal/downloads
Restart=on-failure
RestartSec=5s
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/billet/portal /var/lib/billet -/var/lib/billet-portal
[Install]
WantedBy=multi-user.target
Then start it:
sudo systemctl daemon-reload
sudo systemctl enable --now billet-portal
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:5150/_health
The last command prints 200 when the portal is up. If it does not, read the log:
journalctl -u billet-portal -b --no-pager | tail -50
Settings without editing the unit
Every BILLET_ setting can also go in /etc/billet/settings.toml, as a key without the prefix: public_host = "portal.example.com" is BILLET_PUBLIC_HOST. The service's environment wins over the file. The portal's start-up log lists each setting it took from the file.
Back up
Back up the PostgreSQL database, /etc/billet and /var/lib/billet-portal together. The database holds every tenant, desktop and assignment; /etc/billet holds the secrets that read parts of it; /var/lib/billet-portal/ca is the certificate authority every host and relay trusts.
SQLite, for a lab or test only
For a lab or a test deployment that nobody depends on, the portal can keep its database in a SQLite file instead. Skip step 2, and in step 6 use these two lines in place of the PostgreSQL ones:
DATABASE_URL=sqlite:///var/lib/billet/billet.sqlite?mode=rwc
DB_MAX_CONNECTIONS=1
DB_MAX_CONNECTIONS must stay 1 for a SQLite lab or test portal: with more, a new database cannot be created. A lab portal on SQLite cannot have a second node. Start any deployment people will rely on with PostgreSQL, rather than planning to move a lab across later.