2. Installation
This guide describes Lawliet 1.5.13.s (the current release). Screens, commands and procedures may change in later versions; the usage guide is updated with each release. Check the version your server runs under Settings > Platform or with ./lawliet version, and see the Releases page for what changed.
From the bundle on a download link to a signed-in, licensed console with a second approver.
| You need | |
|---|---|
| Who | A server administrator with root (or sudo) on the Lawliet server |
| Console role | None until step 2.9; then the Super administrator account the installer creates |
| Four-eyes | Not needed for anything in this chapter (there is only one approver yet) |
| Time | About 30 minutes, most of it waiting for the first start |
Every command in this guide is run from the installation directory, as root or with sudo. Use the same account every time: .env is readable only by the account that wrote it.
2.1 Before you start#
Server requirements#
| Item | Requirement |
|---|---|
| Operating system | Linux on Intel/AMD (x86_64, bundle folder amd64) or ARM (aarch64, bundle folder arm64). Check with uname -m |
| Docker | Docker Engine 24 or later with the Compose plugin (docker compose), or docker-compose. Installed and running before you start. The bundle does not contain Docker |
| Tools | bash, curl, tar, and sha256sum (or shasum). openssl to generate a self-signed certificate and to check update signatures |
| CPU, memory | 4 CPU and 8 GB RAM for a small estate; more for a large one |
| Disk | 40 GB free recommended. The installer warns below 10 GB. An update later needs about twice the bundle size free in the parent directory |
| Time | NTP configured (timedatectl shows System clock synchronized: yes) |
| Internet | Not needed, ever |
The bundle must match the server's CPU. A bundle can carry both architectures, or one (its name then ends in -amd64 or -arm64).
Decide three things first#
- The address. The host name or IP address operators will type in the browser and agents will connect to. See 2.7 Install by IP address or by name.
- HTTPS or plain HTTP. Use HTTPS. Plain HTTP sends passwords and tokens across the network in clear text and exists for a laptop lab.
- The certificate. Your own (from your CA), or a self-signed one the installer makes. See 2.8 Certificate choices.
Firewall requests to raise#
| From | To | Port | Needed for |
|---|---|---|---|
| Operator workstations | Lawliet server | 443/tcp (80/tcp optional, it redirects) | Console |
| Monitored hosts | Lawliet server | 443/tcp | Agents |
| Lawliet server and monitored hosts | Your NTP source | 123/udp | Command signing |
| Log senders | Lawliet server | 5514/udp and 5514/tcp | Only if you collect syslog |
In plain-HTTP mode operators need both 3000/tcp and 8000/tcp, and agents need 8000/tcp. The full list, including optional outbound rules, is in Security hardening of the server.
2.2 Install Docker on a server without internet#
./lawliet install checks for Docker and stops if it is missing. It does not install it.
On a machine with internet access, download from Docker's package site (download.docker.com) the packages for the server's distribution, version and CPU:
containerd.io,docker-ce,docker-ce-cli,docker-buildx-plugin,docker-compose-plugin(.debfor Debian and Ubuntu,.rpmfor RHEL, Rocky, Alma). An internal mirror that carries Docker CE works too.Copy them to the server and install them together:
sudo dpkg -i ./*.deb # Debian, Ubuntu sudo dnf install ./*.rpm # RHEL, Rocky, AlmaStart Docker and check it:
sudo systemctl enable --now docker sudo docker info sudo docker compose version
What you see: docker info prints the server details without an error, and docker compose version prints a version.
If it fails: Cannot connect to the Docker daemon means the service is not running (sudo systemctl status docker). docker: 'compose' is not a docker command means the docker-compose-plugin package is missing.
2.3 Download the bundle and check it#
Your vendor sends download links that expire after a few days. Each link resumes after a dropped connection. You receive:
| File | What it is |
|---|---|
lawliet-docker-<version>-<customer>.tar.gz | The bundle |
lawliet-docker-<version>-<customer>.tar.gz.sha256 | Its SHA-256 checksum |
INSTALL-NOTES.txt, installation-guide.html | Notes for this delivery |
On the machine with internet access:
Download each file. To resume a broken download, run the same command again:
wget -c '<link>' # or curl -C - -fLO '<link>'Keep the single quotes: the links contain
&.Check the bundle against its checksum:
sha256sum -c lawliet-docker-<version>-<customer>.tar.gz.sha256Copy the bundle and the
.sha256file to removable media, carry them to the server and check again there (step 2 of 2.4).
What you see: lawliet-docker-<version>-<customer>.tar.gz: OK
If it fails: FAILED means the file is damaged or incomplete. Download it again with wget -c (it continues) or from the start. Do not install a bundle that fails this check.
2.4 Unpack the bundle on the server#
Choose a parent directory with room for two versions side by side, for example /opt. An update unpacks the new version beside the current one.
Copy the bundle and its
.sha256file to the parent directory.Check the checksum on the server:
cd /opt sha256sum -c lawliet-docker-<version>-<customer>.tar.gz.sha256Unpack and enter the installation directory:
sudo tar xzf lawliet-docker-<version>-<customer>.tar.gz cd lawliet-<version>/
What you see: a directory lawliet-<version> containing lawliet, docker-compose.yml, images/, agents/, docs/ and more.
If it fails: gzip: stdin: unexpected end of file means the copy is incomplete. Copy it again from the original file.
2.5 Run the installer#
sudo ./lawliet installThe installer asks a few questions, shows a summary, and writes nothing until you confirm the summary. Press Enter to take the default shown in brackets.
Step by step#
Checking this machine. No question. It checks Docker, Compose,
curl, free disk, that this is a complete bundle, that the bundle carries images for this CPU and that every image file is present, and that no deployment with the same project name exists.Licence. No question. It says whether a licence is embedded in the bundle. Most bundles have none: the licence is issued for this installation after it is running (step 2.10).
Existing configuration. Only when
.envalready exists: Overwrite it and configure from scratch? Default no. Yes keeps the old file as.env.backup.<timestamp>.Where will operators reach this server? Type the address. The default is this server's detected IP address. Check it: if it shows
localhostor the wrong network card, type the right address.How should it be served?
Choice Meaning 1(default)HTTPS on 443 through the bundled nginx. Recommended 2Plain HTTP on 3000 and 8000. Lab use only Host ports. Only shown when 80/443 (HTTPS) or 3000/8000 (plain HTTP) are already in use. The installer picks the next free port and uses it in every URL and command it prints.
TLS certificate (HTTPS only). See 2.8.
Choice Then it asks 1(default)Path to the certificate (fullchain PEM): and Path to the private key (PEM): 2Nothing. It generates a self-signed certificate Administrator account.
- Username: default
admin. - Generate a strong password for me? Default yes (20 characters, shown once at the end). Answer
nto type one: 12 to 128 characters, not a known default or a placeholder, typed twice.
- Username: default
Generating secrets. No question. It generates the JWT signing key, the data encryption key, the database password and the agent enrolment secret.
Ready to install. A summary: address, console URL, serving mode, administrator. Write this configuration and start the platform? Default yes.
Loading images. It checks each image file against its checksum and loads it into Docker. Nothing is downloaded.
Starting and Waiting for services. A live list of services with their state. This takes a few minutes on the first start; the installer waits up to 7 minutes.
Verifying the install. It checks that the API and the login page answer at the address you gave, and that the database schema is stamped.
What you see at the end#
+-----------------------------------------------------------+
| Installed and running |
+-----------------------------------------------------------+
Console https://10.0.0.5
Username admin
Password <the password>
! Save that password now. It is stored in .env and not shown again.Then, in order:
- Firewall rules you need: the ports this install actually uses.
- Adding your first agent: a one-line Linux install command. Agents are covered in Agents overview.
- Activate your licence: the installation fingerprint (only when the bundle has no licence).
- From here: the everyday commands.
Save the administrator password now in your password safe. If you lose it, see Troubleshooting.
If it fails#
| Message | Cause and fix |
|---|---|
Docker is not installed or Docker is installed but not running | Do 2.2, then run the installer again |
curl is not installed | Install it from the OS media (apt-get install curl, dnf install curl) |
Only N GB free here | Free disk space, or answer yes to continue (not recommended below 10 GB) |
This does not look like a Lawliet bundle: <file> is missing | The unpack is incomplete. Unpack the bundle again |
This bundle has no images for this server (<arch>) | The bundle was built for the other CPU. Ask your vendor for a bundle for this CPU |
Bundled image <file> is missing or failed its checksum | The copy is damaged. Copy the bundle again and check its .sha256 |
Not enough disk space to load the images | Free space where Docker keeps its data (docker info shows the directory) |
A Lawliet deployment named 'lawliet' already exists on this host | See 2.6 |
That certificate and key do not match | The key belongs to another certificate. Give the matching pair |
Not everything became healthy within 7 minutes | Run sudo ./lawliet logs backend and sudo ./lawliet doctor. See Troubleshooting |
Installed, but the checks above did not pass | Fix what is listed above that line, then sudo ./lawliet restart and sudo ./lawliet status |
API answering on this host, but not at <url> (a warning, not a failure) | The platform is running, but this server cannot reach itself by that address. Normal behind NAT or split DNS. Check from an agent host before enrolling: curl -k https://<address>/health |
If you stop the installer before the summary, nothing is written. Run it again.
2.6 Two deployments on one host#
Container names and data volumes are tied to the Compose project name (lawliet by default). A second deployment on the same host, for example a staging copy, needs its own name. Use the same variable for every command in that directory:
sudo COMPOSE_PROJECT_NAME=lawliet-staging ./lawliet install
sudo COMPOSE_PROJECT_NAME=lawliet-staging ./lawliet statusThe installer moves the second deployment's host ports to the next free ones and prints them.
If the installer stops with A Lawliet deployment named 'lawliet' already exists on this host, the existing containers are in the way. Stopping them is not enough (a stopped container keeps its name). Your choices, as the installer lists them:
- install this one under its own name (above), or
- remove the old deployment from its own directory with
./lawliet uninstall(this deletes its database), or - remove just the old containers, keeping its data:
docker rm <the names it lists>.
2.7 Install by IP address or by name#
| By IP address | By host name | |
|---|---|---|
| What operators type | https://10.0.0.5 | https://lawliet.corp.example |
| Needs | Nothing | A DNS record (or hosts-file entry) that operator workstations and every agent host resolve |
| Certificate | Must name the IP address as an IP entry. A self-signed certificate made by the installer always does | Must name the host name. A self-signed certificate made by the installer also names this server's IPv4 addresses |
| Air-gapped site without DNS | Recommended | Only if you maintain hosts files everywhere |
Whichever you choose, the installer:
- sets
CORS_ORIGINSto the console address you gave, and - sets
TRUSTED_HOSTSto that address, plus this server's own IPv4 addresses,localhostand127.0.0.1.
The backend refuses any request whose host name is not in TRUSTED_HOSTS with 403 Invalid host header. This is why an agent can still reach a server installed by name through its IP address.
Avoid localhost as the address. The installer warns: localhost is reachable only from this machine. Agents on other hosts cannot enrol against it, and browsers elsewhere cannot open the console.
2.8 Certificate choices#
| Choice | When to use it | What agents need |
|---|---|---|
| Your own certificate from your CA (recommended) | Your browsers and agent hosts already trust that CA | Nothing extra |
| Self-signed, generated by the installer | No CA available, or to start quickly | Each agent host must be given the certificate: sudo ./lawliet tls --export writes lawliet-server.pem |
| Plain HTTP (no certificate) | A lab laptop only | Installers add --insecure. Not for a real network |
Your own certificate. Give the full chain (server certificate first, then intermediates) and the private key, both in PEM. The installer checks that they match and shows the expiry date. If agents will connect by IP address, the certificate must carry that address as an IP subjectAltName.
Self-signed. RSA 2048, valid 825 days, naming the address you gave, this server's own IPv4 addresses and localhost. Browsers warn until it is replaced. The installer prints Agents must trust it too. Run: ./lawliet tls --export.
A certificate already present in infra/docker/ssl/fullchain.pem and privkey.pem is kept as it is. Use this to move an existing certificate onto a new server.
You can replace, re-issue or switch the certificate later without reinstalling. See TLS and certificates.
2.9 First sign-in#
Open the console URL from your workstation (not from the server: localhost works only there). With a self-signed certificate the browser warns once. Before you accept it, compare the fingerprint the browser shows with the one on the server:
openssl x509 -noout -fingerprint -sha256 -in infra/docker/ssl/fullchain.pemStep 1: sign in#
- On the Sign in page, enter the administrator username and password from the installer.
- Press Sign in.
What you see: the Set a new password page.
Step 2: set a new password#
The install-time password must be replaced, even a generated one. The page says This password was set at install time, not by you.
- Enter Current password (the installer's), New password and Confirm new password.
- Press Set password.
If it fails: the new password must be different from the current one, and the two new entries must match. The page shows which check failed.
Step 3: turn on two-factor authentication#
Required for every local account (REQUIRE_TWO_FACTOR=true, the default).
- 1. Scan with an authenticator app. Scan the QR code with Google Authenticator, Microsoft Authenticator, 1Password or any TOTP app. If you cannot scan, use the key shown under Can't scan? Enter this key:.
- 2. Save your recovery codes. Ten codes, each works once, shown only now. Press Copy or Download and store them offline. Tick I have saved these codes somewhere safe.
- 3. Enter the 6-digit code from the app.
- Press Turn on two-factor authentication.
If it fails: That code was not accepted. Check the time on your phone and try the next one. The code is checked against the server's clock with no tolerance window, so the phone's time must be correct. Wait for the next code and try again.
Step 4: the activation screen (only without a licence)#
If no licence is active, the console shows Activate this installation. Continue with 2.10. Until then nothing else in the console opens.
Step 5: create the second approver#
Until two approvers who can approve each other exist, every page shows Four-eyes is inactive until a second approver exists. End this state on the first day.
- Go to Settings > Users.
- Under Add an account, enter the username, email and a temporary password, and choose the role.
- Press Create user.
Choose the role so that the two of you can approve each other's user and policy changes:
| You are | Create | Result |
|---|---|---|
| Super administrator (the install account) | A second Super administrator | Each approves the other |
| Super administrator | An LSO, then an RSO | The LSO and RSO approve each other; keep the Super administrator account sealed for recovery |
A Super administrator and an LSO alone are not a pair (an LSO is approved by an RSO). Details: Users, roles and approvals.
- The new person signs in on their own device with the temporary password and enrols two-factor (required). Then they replace the temporary password on the page Change your password at
https://<address>/change-password.
What you see: once the pair exists, the banner disappears. From then on, critical actions wait under Approvals.
If it fails: 409 MANAGEMENT_WOULD_LOCK means the account you tried to make the first second approver could not approve user management or policy changes. Choose one of the pairs above.
2.10 Activate the licence#
| You need | |
|---|---|
| CLI route | Root on the server. No four-eyes |
| Console route | Permission Manage the licence. Four-eyes: Change the licence (on the first day it runs on your one signature) |
Step 1: send the fingerprint to your vendor#
Read the installation fingerprint, either way:
sudo ./lawliet licenceor in the console on the activation screen (or Settings > Platform, panel License) under Installation fingerprint, with a copy button. The console also shows Request for your vendor: a ready text with the fingerprint, version, host name and date.
Send the fingerprint (or the request text) to your vendor. It identifies the installation and nothing else.
Step 2: install the key you receive#
From the server (the key, or a file that contains it):
sudo ./lawliet licence /path/to/customer.keyWhat you see:
> Checking the new licence
OK complete licence for <organisation>, valid until 2027-10-14
OK Bound to this installation
OK Written to .env (previous file kept as .env.backup.*)
> Applying
OK Licence in effectFrom the console: on the activation screen, or Settings > Platform, panel License, paste the key into Licence key / Enter a new licence key and press Activate licence. Once a second approver exists, this goes to Approvals first: Sent for approval. It is activated once signed off and executed.
If it fails#
| Message | Cause and fix |
|---|---|
That is not a licence key. A key starts with LAWLIET-. | Paste or point to the whole key |
The licence is not valid yet | The server clock is behind the licence start time. Fix the clock (see Troubleshooting), then install it again |
| A key bound to another installation is refused | The key was issued for a different fingerprint. Send this installation's fingerprint again |
This key was issued ... before the licence in use | An older key cannot replace a newer one. Nothing was changed |
The backend is not answering | Start the platform: sudo ./lawliet start |
2.11 The setup checklist#
Settings > Setup lists what is still open on a new installation:
| Step | Done when | Button |
|---|---|---|
| Set an administrator password | The install-time password is replaced | Set password |
| Activate a licence | A valid licence is active | Open settings |
| Configure outbound email | SMTP_ENABLED=true with SMTP_HOST, SMTP_PORT and SMTP_FROM_EMAIL in .env (optional; needed for password-reset links and scheduled reports) | - |
| Enrol your first agent | A real agent has registered | Install an agent |
After editing .env, apply it with sudo ./lawliet start. A restart does not re-read .env.
2.12 Next steps#
- Check the installation:
sudo ./lawliet status(CLI reference). - Take a first backup and copy it off the server (Backup and restore).
- Install agents (Agents overview).
- Walk through Security hardening of the server.
Removing an installation#
See CLI reference: uninstall. It deletes the database.

