Skip to main content

Single-Machine Installation

This page covers two ways to deploy a single-machine Runtime on your own Linux server: use the Soln installer over SSH, or use the CLI in the server terminal. For a first deployment, follow the installer workflow below. Use the CLI tutorial when you need to manage server configuration yourself. For installation on your own computer, see Local Installation.

Deploy a Cloud Server with the Soln Installer​

Use Soln installer 1.0.1 or later on your own computer to connect to and deploy the server. You do not need Docker on that computer or a preinstalled Soln CLI on the server. Runtime, its database, and business files run on the target server.

Before you start​

  • Server: automatic deployment currently supports Ubuntu 24.04 with systemd on x86_64 or ARM64. Prefer a dedicated server. If existing containers or deployment files are found, verify ownership before proceeding.
  • SSH: prepare the server address, username, port, and password or private key. The user must be root or have passwordless sudo. Your computer must be able to reach the address; a private IP requires a reachable local network or VPN.
  • Soln account: prepare an existing workspace and a member with tenant.runtime.deployment.manage. Sign in and authorize the workspace in your browser.
  • Access address and ports: prepare a complete HTTPS address, such as https://runtime.example.com. Point its DNS to the server and allow SSH, port 80, and port 443 through the cloud security group and host firewall. Automatic certificate validation requires public access on port 80.
  • Capacity and network: the server must reach the Control Plane, software sources, certificate service, and image downloads. Plan capacity for business data, image downloads and extraction, and upgrades. Resource recommendations are not guarantees of installation success or failure.

HTTPS domains and IP addresses are accepted. Automatic IP certificates require a publicly verifiable IP; this workflow cannot issue a public trusted certificate for a private IP. For mainland China domains, also confirm filing and access requirements and ensure public requests are not redirected to a blocking page. See Server Host Preparation.

Step 1: Connect to the cloud server​

  1. Open the Soln download page on your computer and install the package for that computer’s operating system and CPU architecture.
  2. Open Soln and choose Deploy on a cloud server from Register workspace. From an existing workspace list, use Register more workspaces.
  3. Enter the server address and SSH username. Choose Password or SSH private key and provide the corresponding credentials. The server address is an IP or hostname without a protocol, port, or path. Set the SSH port in Advanced settings; the default is 22.
  4. Click Connect and check. On first connection, compare the SSH host fingerprint with the cloud console or server administrator, then click Trust and connect when it matches.
  5. Review the operating system, resources, Docker, and existing deployment checks. This stage only reads server information. Missing Docker is handled during deployment. Resolve blocking checks before continuing.

Reinstalling a server usually changes its SSH host keys. If a changed-key warning appears, verify the new fingerprint through the cloud console before establishing trust again. An unchanged IP does not verify server identity.

Step 2: Sign in and prepare storage​

  1. Click Sign in to Soln, finish browser sign-in, and authorize the target workspace. Wait for its information to return to the installer.
  2. Enter the complete HTTPS Access address, without a business path, query, or fragment. Advanced settings lets you change the installation directory, which defaults to /opt/soln-tenant-runtime. The data directory defaults to /srv/soln/data.
  3. Choose one storage method from the dropdown.
Storage methodRequired actionReady state
Automatic encrypted storageClick Check available capacity, select a volume size within the displayed range, and click Create encrypted storage after workspace sign-inLUKS2 volume mounted and automatic unlock configured
Existing cloud-encrypted diskConfirm encryption in the cloud console, mount the disk on the server, enter its data directory, check the disk, and confirm encryptionMount and boot checks passed, with provider encryption confirmed

Automatic storage creates a LUKS2 file volume on an existing server disk; a separately purchased cloud disk is not required. Its creation minimum is 11 GiB. The selectable range depends on free space and the host reserve, and the minimum is not the capacity required for a complete installation. The volume consumes backing-disk space rather than adding capacity. Review First deployment capacity plan before creating it. An identified existing volume is reused, with expansion offered when capacity permits; it is not reformatted.

The unlock key is stored on the server in a root-only location for automatic unlock after reboot. A full-server backup may contain both the volume and its key; protect those backups. Verify provider encryption in the cloud console, because SSH checks cannot independently prove it.

A first deployment configures encrypted data storage for business data and container images. Existing image stores are not automatically migrated by capacity planning; use View workspace details after deployment when migration is required. The system disk retains the OS and installer tools. See Storage and Capacity Management.

  1. Accept the certificate service terms. If the workspace already has a Runtime registration, also confirm rebinding. Rebinding invalidates the previous connection credentials; verify the workspace and server before confirming.
  2. Click Start deployment once workspace authorization, server checks, encrypted storage, and required confirmations are ready.

Step 3: Install and open the workspace​

The installer prepares server dependencies, configures HTTPS and automatic certificate renewal, runs a renewal rehearsal, obtains the deployment package, installs images and services, and checks the actual HTTPS address. Sources are listed in Deployment Images.

Installing images and services displays an estimate of 10–30 minutes and elapsed time. This is a planning estimate; download speeds, cached images, and server resources affect duration. Follow the actual logs and task status.

  • During installation: Cancel requests termination of this installation; the right button displays Installing. Cancellation does not roll back completed steps or delete existing files and services.
  • Connection lost or status unavailable: the task runs on the server. A lost connection does not mean it stopped. Reconnect and check the original task before retrying; do not start a duplicate deployment.
  • Installation failed: preserve logs and files, resolve the error, and click Check and retry. Use Back to edit deployment inputs. Keep the same workspace, access address, and directories when resuming.
  • Deployment completed: after installation and HTTPS health checks pass, the address is added to the workspace list. Click Open workspace. If list saving fails, click Retry saving workspace.

After successful health checks, targeted cleanup removes temporary downloads confirmed unnecessary for this task, and installation logs are bounded. Business data, deployment configuration, runtime dependencies, and certificate renewal files remain. Migration source data and recovery material require separate review after confirming the deployment works; do not delete data directories or container storage indiscriminately.

Manage the deployed workspace​

Right-click the workspace in the list and choose View workspace details. Connect to the server, sign in to Soln within the page, confirm the installation directory, and click Read server details. Available operations depend on the inspected storage and deployment configuration:

  • Expand encrypted volume: offered only for an identified automatic encrypted file volume; expansion only, within the actual available range. Expand provider-encrypted disks in the cloud console first, then expand the filesystem in the details page when supported or follow provider instructions.
  • Move to another data disk: moves the workspace’s business data directory (including its database and files), container images, and container engine storage to an empty directory on another mounted, encrypted disk on the same server. Workspace services and the container engine stop during copying. After verification, storage switches to the new disk. Original data remains for recovery; review it only after confirming the new storage works.
  • Change access domain: enter and authorize a new HTTPS address in the page. The installer handles server configuration and certificates, and updates the workspace list after completion. Configure DNS and public ports first. If certificate issuance fails, inspect public access and validation logs; upgrading the tenant does not fix DNS or blocking-page issues.

Deploy with the CLI​

Run the CLI tutorial below in the server terminal. Workspaces already deployed successfully with the installer do not need first-time registration and installation again.

Connect to the server over SSH, then run sudo -i for a root shell. Run the npm install -g, sl, and docker commands below in that shell. Existing sudo prefixes also work as root.

Examples use Ubuntu 24.04, /opt/soln-tenant-runtime for deployment files, /srv/soln/data for encrypted data, and runtime.example.com as the public domain. Replace the example domain, tenant subdomain, and email before copying commands.

Before You Start: Collect the Deployment Inputs​

The main route in this guide is Ubuntu 24.04 with systemd and a single-machine server deployment. Use it for a first deployment. Teams with existing databases, Temporal, and object storage can use External Services. Do not copy the package installation commands onto another operating system.

Required inputWhere to get itExample to replace
Server public IP, SSH user, and keyCloud console or server administratorYour actual server address
Tenant subdomainControl Plane tenant informationexample: the tenant identifier, not the access domain or tenant UUID
Member email with deployment permissionA member account in that tenantoperator@example.com; a platform operator account is not a substitute
Control Plane API addressYour environment administratorMainland China example: https://api.solncn.com; use the corresponding address overseas
Tenant access domainDNS you manageruntime.example.com, separate from the optional Custom domain field
Deployment and data directoriesPlan on this server/opt/soln-tenant-runtime and /srv/soln/data
Data volume planConfirm a new empty disk or existing encrypted volumeSeparate data disk, existing encrypted volume, or a file container sized for the workload

Collect these inputs first. Execute one command block at a time and stop on failure. Replace the domain, email, and API address throughout this guide. Use the same server user for installation, deployment login, and operations; this guide uses a root shell.

For production capacity, automatic recovery planning, and expansion, see Storage and Capacity Management.

1. Prepare the server, account, and encrypted storage​

  1. Create the tenant in the Control Plane. Use a tenant member account with tenant.runtime.deployment.manage.
  2. Follow Server host preparation to install Docker Engine, Compose, Buildx, Node.js 22+, and Cosign. Choose the mainland China mirror or official source from Deployment Images: Host Software. Verify docker version, docker compose version, docker buildx version, node -v, and cosign version.
  3. Choose one encrypted storage route below. Prefer a cloud-native encrypted data disk for unattended cloud deployments: the provider handles encryption, and Linux mounts the filesystem by UUID without a LUKS passphrase. With LUKS, unlock before mounting; the passphrase examples below require manual recovery after reboot. Reuse existing storage without formatting it. After preparing the volume, verify that the data directory itself is a mount point:
findmnt /srv/soln/data
stat -c '%a %U:%G %n' /srv/soln/data
df -h / /srv/soln/data

For cloud-native encryption, the source is the data device, such as /dev/vdb or its partition; confirm encryption and the attached disk identity in the cloud console. For LUKS, the source is /dev/mapper/.... In both cases the directory mode must be 700. Permissions and filesystem type do not prove encryption. sl storage status may report encryption: unverified for a cloud-encrypted disk because Linux cannot verify provider encryption. The CLI also requires at least 10 GiB free on the deployment directory's filesystem. Allow more for old and new images during upgrades. Use 2 vCPU and 4 GiB RAM as the entry support configuration for the complete stack, with at least 60 GiB system disk recommended. Validate business workloads separately; see Capacity planning.

Install Cosign from a Mirror​

Download addresses: Deployment Images: File Downloads.

Run in the server's root terminal. GitCode is used by default; for the GitHub backup, replace SOLN_COSIGN_DOWNLOAD_BASE with https://github.com/solnsu/soln-installer-releases/releases/download/v1.0.0:

(
set -e
SOLN_COSIGN_DOWNLOAD_BASE='https://gitcode.com/lunaris_soln/soln-installer-releases/releases/download/v1.0.0'
case "$(uname -m)" in
x86_64) SOLN_COSIGN_ARCH=amd64 ;;
aarch64) SOLN_COSIGN_ARCH=arm64 ;;
*) echo 'Unsupported architecture'; exit 1 ;;
esac
SOLN_COSIGN_TMP=$(mktemp -d)
trap 'rm -rf "$SOLN_COSIGN_TMP"' EXIT
curl -fL --connect-timeout 15 \
"$SOLN_COSIGN_DOWNLOAD_BASE/cosign_checksums.txt" \
-o "$SOLN_COSIGN_TMP/cosign_checksums.txt"
curl -fL --connect-timeout 15 \
"$SOLN_COSIGN_DOWNLOAD_BASE/cosign-linux-$SOLN_COSIGN_ARCH" \
-o "$SOLN_COSIGN_TMP/cosign-linux-$SOLN_COSIGN_ARCH"
(cd "$SOLN_COSIGN_TMP"; sha256sum --ignore-missing -c cosign_checksums.txt)
install -m 0755 "$SOLN_COSIGN_TMP/cosign-linux-$SOLN_COSIGN_ARCH" /usr/local/bin/cosign
cosign version
)

Verification should print OK, and cosign version should report GitVersion: v3.1.2. For other versions, use the official-source installation instructions.

Route A: Cloud-Native Encrypted Data Disk​

In the cloud console, create an encrypted data disk in the instance's region and availability zone, attach it to this instance, and verify its encryption status and required key permissions. On Alibaba Cloud ECS, this is an ECS block-storage disk, not an enterprise file-sharing drive. This route does not add a LUKS layer in Linux.

Inspect the disk and match its serial number or stable device identifier to the cloud console:

lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,UUID,SERIAL,MOUNTPOINTS
ls -l /dev/disk/by-id/
findmnt /srv/soln/data

Existing data: use its existing filesystem and skip formatting. If a tenant already uses another data volume, follow the storage migration procedure before changing its mount; the new-disk steps below do not migrate data.

New empty disk only: replace the path with the confirmed disk's stable identifier. The block rejects partitions, existing filesystem signatures, mounts and an installed tenant. It formats the whole empty data disk as ext4:

SOLN_DATA_DEVICE='/dev/disk/by-id/REPLACE_WITH_CONFIRMED_EMPTY_DATA_DISK'
(
set -e
test -b "$SOLN_DATA_DEVICE"
test "$(lsblk -dn -o TYPE "$SOLN_DATA_DEVICE")" = disk
test "$(lsblk -nr -o NAME "$SOLN_DATA_DEVICE" | wc -l)" -eq 1
test -z "$(lsblk -nr -o MOUNTPOINTS "$SOLN_DATA_DEVICE")"
test ! -f /opt/soln-tenant-runtime/.soln/deployment.json
test ! -e /dev/mapper/soln-tenant-data
if mountpoint -q /srv/soln/data; then
echo 'Data directory is already mounted; stopping'
exit 1
fi
if [ -d /srv/soln/data ]; then
test -z "$(find /srv/soln/data -mindepth 1 -maxdepth 1 -print -quit)"
fi
SOLN_DATA_SIGNATURES=$(wipefs --no-act --noheadings --output TYPE "$SOLN_DATA_DEVICE")
test -z "$SOLN_DATA_SIGNATURES"
lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,SERIAL,MOUNTPOINTS "$SOLN_DATA_DEVICE"
read -r -p 'Confirm this cloud-encrypted disk is empty; enter its full path: ' SOLN_CONFIRMED_DEVICE
test "$SOLN_CONFIRMED_DEVICE" = "$SOLN_DATA_DEVICE"
mkfs.ext4 -m 0 -L soln-tenant-data "$SOLN_DATA_DEVICE"
)

Configure persistent mounting in a separate step. For an existing ext4 volume, set SOLN_DATA_DEVICE to its actual filesystem device or partition first. Run this only before installation, with the target directory unmounted and empty. An existing /etc/fstab entry for this path must be reviewed rather than appended again:

(
set -e
test -b "$SOLN_DATA_DEVICE"
test "$(blkid -s TYPE -o value "$SOLN_DATA_DEVICE")" = ext4
test -z "$(lsblk -nr -o MOUNTPOINTS "$SOLN_DATA_DEVICE")"
test ! -f /opt/soln-tenant-runtime/.soln/deployment.json
if mountpoint -q /srv/soln/data; then
echo 'Data directory is already mounted; stopping'
exit 1
fi
if [ -d /srv/soln/data ]; then
test -z "$(find /srv/soln/data -mindepth 1 -maxdepth 1 -print -quit)"
fi
awk '$1 !~ /^#/ && $2 == "/srv/soln/data" { found=1 } END { exit found ? 1 : 0 }' /etc/fstab
SOLN_DATA_UUID=$(blkid -s UUID -o value "$SOLN_DATA_DEVICE")
test -n "$SOLN_DATA_UUID"
cp -a /etc/fstab "/etc/fstab.soln-backup-$(date +%Y%m%d%H%M%S)"
install -d -m 0700 /srv/soln/data
printf '\nUUID=%s /srv/soln/data ext4 defaults,nofail,x-systemd.device-timeout=30s 0 2\n' "$SOLN_DATA_UUID" >> /etc/fstab
systemctl daemon-reload
mount /srv/soln/data
test "$(findmnt -n -o UUID --mountpoint /srv/soln/data)" = "$SOLN_DATA_UUID"
chmod 0700 /srv/soln/data
findmnt -o TARGET,SOURCE,FSTYPE,UUID --mountpoint /srv/soln/data
df -h / /srv/soln/data
)

Record the filesystem UUID for restart checks. nofail allows the host to boot for diagnosis if the disk is unavailable; Docker mount protection below still prevents tenant startup without the mount. If mounting fails after formatting or writing fstab, fix that step without formatting again. Skip Route B and continue to Configure Docker Mount Protection.

Route B: LUKS with a Manual Passphrase​

Choose this route when the host manages encryption itself. These examples do not configure unattended unlocking; use an administrator-managed external key service if that is required. See Restart Recovery and Key Management.

A New Empty Data Disk or an Existing LUKS Volume​

Inspect without formatting:

lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,MOUNTPOINTS
findmnt /
findmnt /srv/soln/data
  • Existing encrypted volume: use the mapping and mount point supplied by your administrator. Unlock, mount, and verify it; do not run the formatting commands below.
  • New empty disk: compare its size and device path with the cloud console and inspection output. Confirm it is not the system disk, is not mounted, and contains no data to preserve. Stop and consult your administrator if you cannot confirm this.

The following steps erase the selected device. Only after confirming the disk can be erased, set its actual path; the example never selects a device automatically:

SOLN_DATA_DEVICE='/dev/REPLACE_WITH_CONFIRMED_EMPTY_DATA_DISK'

Then run in the same root shell:

(
set -e
test -b "$SOLN_DATA_DEVICE"
test ! -e /dev/mapper/soln-tenant-data
if mountpoint -q /srv/soln/data; then
echo 'Data directory is already mounted; stopping'
exit 1
fi
lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,MOUNTPOINTS "$SOLN_DATA_DEVICE"
read -r -p 'Confirm this device can be erased; enter its full path: ' SOLN_CONFIRMED_DEVICE
test "$SOLN_CONFIRMED_DEVICE" = "$SOLN_DATA_DEVICE"
apt-get update
apt-get install -y cryptsetup
cryptsetup luksFormat "$SOLN_DATA_DEVICE"
cryptsetup open "$SOLN_DATA_DEVICE" soln-tenant-data
mkfs.ext4 -m 0 /dev/mapper/soln-tenant-data
install -d -m 0700 /srv/soln/data
mount /dev/mapper/soln-tenant-data /srv/soln/data
chmod 0700 /srv/soln/data
findmnt /srv/soln/data
)

Record the device path, mapping name, and unlock passphrase for restart recovery. After success, skip the file container below and continue to Configure Docker Mount Protection.

Optional: A Small LUKS File Container​

Without a separate data disk, you can create a LUKS file container on the system disk. The 8 GiB example provides roughly 7.8 GiB after formatting for database, attachments, and workflow data; it is not a universal production capacity requirement. Existing containers with sufficient capacity and backups can remain in production use. Prefer separate data disks for new enterprise deployments. Enlarging the system disk does not enlarge the container.

Use this only when /var/lib/soln/tenant-data.luks does not exist and no tenant is installed. Never format an existing data volume. Check free disk space first and run the command block. cryptsetup luksFormat prompts you to set an unlock passphrase:

(
set -e
df -h /
apt-get install -y cryptsetup
if [ -e /var/lib/soln/tenant-data.luks ] || [ -e /dev/mapper/soln-tenant-data ] || mountpoint -q /srv/soln/data; then
echo 'Existing data file or mount found; stop without formatting it'
exit 1
fi
install -d -m 0700 /var/lib/soln /srv/soln/data
fallocate -l 8G /var/lib/soln/tenant-data.luks
chmod 0600 /var/lib/soln/tenant-data.luks
cryptsetup luksFormat /var/lib/soln/tenant-data.luks
cryptsetup open /var/lib/soln/tenant-data.luks soln-tenant-data
mkfs.ext4 -m 0 /dev/mapper/soln-tenant-data
mount /dev/mapper/soln-tenant-data /srv/soln/data
chmod 0700 /srv/soln/data
findmnt /srv/soln/data
)

Keep the passphrase. The file shares the system disk's physical space; it does not add 8 GiB of capacity. After a reboot, run cryptsetup open and mount again before starting the tenant containers.

Configure Docker Mount Protection​

Complete this section after any of the storage routes above.

On a server dedicated to this Docker deployment, prevent Docker starting without the mount:

sudo install -d /etc/systemd/system/docker.service.d
printf '[Unit]\nRequiresMountsFor=/srv/soln/data\nAssertPathIsMountPoint=/srv/soln/data\n' |
sudo tee /etc/systemd/system/docker.service.d/10-soln-data-mount.conf
sudo systemctl daemon-reload
sudo systemctl enable docker.service

For Route A, Docker waits for the UUID mount configured in fstab; no Linux unlock passphrase is required. For Route B, this dependency does not supply a LUKS key: manually unlock and mount after reboot, or configure an external key service before installation. Do not set this daemon-wide condition on a shared Docker host. Reboot recovery must be verified after installation as described in section 6.

2. Set up DNS and HTTPS​

Create an A record for runtime.example.com pointing to the server's public IPv4 address. Allow inbound TCP 80 and 443 in the instance’s associated security group and host firewall. On Alibaba Cloud ECS, HTTP(80) is equivalent to custom TCP 80, and HTTPS(443) to custom TCP 443. Use 0.0.0.0/0 as the source for those public web ports; restrict SSH to administrator addresses. Software sources are listed in Deployment Images: DNS and HTTPS. On Ubuntu, install nginx and Certbot, then check DNS:

sudo apt-get update
sudo apt-get install -y nginx certbot
sudo systemctl enable --now nginx
getent ahostsv4 runtime.example.com
sudo systemctl is-active nginx

Inspect and reuse existing Nginx configuration or certificates for the domain. The creation and append commands below are for initial setup. Retry only Certbot after a certificate failure; do not overwrite existing configuration or append duplicate HTTPS server blocks.

Serve the ACME challenge directory first. Replace the domain below with yours:

sudo install -d -m 0755 /var/www/letsencrypt
sudo tee /etc/nginx/sites-available/soln-tenant.conf >/dev/null <<'EOF'
server {
listen 80;
server_name runtime.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type text/plain;
try_files $uri =404;
}
location / { return 301 https://$host$request_uri; }
}
EOF
sudo ln -sfn /etc/nginx/sites-available/soln-tenant.conf /etc/nginx/sites-enabled/soln-tenant.conf
sudo nginx -t && sudo systemctl reload nginx
sudo certbot certonly --webroot -w /var/www/letsencrypt -d runtime.example.com

Certbot prompts for an email for certificate notices. After it issues the certificate, keep the port 80 block above and append HTTPS configuration. Replace the domain in both the server name and certificate paths:

sudo tee -a /etc/nginx/sites-available/soln-tenant.conf >/dev/null <<'EOF'

server {
listen 443 ssl;
server_name runtime.example.com;
ssl_certificate /etc/letsencrypt/live/runtime.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/runtime.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header 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 "upgrade";
proxy_read_timeout 300s;
}
}
EOF
sudo nginx -t && sudo systemctl reload nginx

HTTPS may return 502 before installation: TLS is working, but no Runtime is listening on port 8080 yet. After installation it should return 200.

Configure Certificate Renewal and Nginx Reload​

With certbot certonly --webroot, Nginx needs to reload after a successful renewal. Once the HTTPS configuration passes nginx -t, run:

sudo install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy
sudo tee /etc/letsencrypt/renewal-hooks/deploy/soln-nginx-reload >/dev/null <<'EOF'
#!/bin/sh
set -eu
/usr/sbin/nginx -t
/usr/bin/systemctl reload nginx
EOF
sudo chmod 0755 /etc/letsencrypt/renewal-hooks/deploy/soln-nginx-reload
sudo systemctl enable --now certbot.timer
sudo systemctl list-timers --all certbot.timer

The timer should show a next run. The hook runs after successful renewal and stops if the Nginx configuration check fails. Test renewal and the hook:

sudo certbot renew --cert-name runtime.example.com --dry-run --run-deploy-hooks

Use the actual Certificate Name from certbot certificates; it may include a -0001 suffix. Expect successful simulated renewal and a passing Nginx check and reload. On failure, inspect DNS, the ACME webroot, the security group, the timer, and Nginx configuration, then retry. The dry run uses staging certificates; the hook reloads Nginx without replacing the live certificate with a staging certificate.

3. Sign in and prepare the deployment​

Install the CLI on the server and sign in as the tenant member. Choose a registry from Deployment Images: CLI. On a mainland China server, use npm install -g @soln/cli@latest --registry=https://registry.npmmirror.com instead of the default command below. Compare the package version with npm before installing. This example uses email verification; omit --method email to use a password:

npm install -g @soln/cli@latest
sl version
sl deployment login --base-url https://api.solncn.com \
--tenant example --email operator@example.com --method email
sl deployment status --json

Verify the tenant shown. For a first installation:

sl deployment register --dir /opt/soln-tenant-runtime \
--deployment-target server --deployment-mode singleMachine \
--runtime-origin https://runtime.example.com
sl deployment prepare --dir /opt/soln-tenant-runtime

The Origin must match the public browser address and must not contain a path or query. The .soln-install-setup.json file contains the Agent secret and should have mode 600. If deployment status already shows a registration, determine whether an earlier installation is incomplete. Do not use register --force merely to retry: it replaces the Agent secret.

4. Confirm encryption and install​

Download addresses are listed in Deployment Images. Complete the following image preparation steps after deployment prepare and before installation.

Import Public Dependency Packages​

Skip this step when the server can reach official registries; the installer pulls the images. Otherwise, choose an archive matching the server architecture from Public Dependency Package Downloads. Run the matching block in the download directory. The archive contains PostgreSQL, MinIO, the MinIO client, Temporal, and nginx. Docling and Soln product images are separate. Do not import the source archive into Docker.

# AMD64 / x86_64
(
set -e
printf '%s %s\n' \
'a2ecb08a4a23854e36d159f65c7a30ce1f80eed27e3ae83736ff9d6eb6d00a93' \
'soln-public-deps-linux-amd64.tar.gz' | sha256sum -c -
docker load -i soln-public-deps-linux-amd64.tar.gz
)
# ARM64
(
set -e
printf '%s %s\n' \
'433ad57439730b3bbbf60c2e4bad134fae0340f958018c3e5d5e4c6fbfc1d592' \
'soln-public-deps-linux-arm64.tar.gz' | sha256sum -c -
docker load -i soln-public-deps-linux-arm64.tar.gz
)

Verification should print OK, followed by five loaded images. Reserve disk space for the unpacked images before importing. After successful import, remove the downloaded archive if needed and check space with df -h /opt /srv/soln/data and docker system df. Keep the imported images required for installation.

Select the Docling Registry​

See Deployment Images: Docling for addresses. Keep the generated deployment file for the official source. For the mainland China mirror, run the following replacement after deployment prepare and before the first installation, preserving the image digest:

sed -i \
's#quay.io/docling-project/docling-serve-cpu@sha256:061d35c03611bc15b73d024c8e8387bcf0624279f8b57c16c1567326f214ba56#quay.nju.edu.cn/docling-project/docling-serve-cpu@sha256:061d35c03611bc15b73d024c8e8387bcf0624279f8b57c16c1567326f214ba56#' \
/opt/soln-tenant-runtime/docker-compose.yml

Compare the generated Docling reference with the example before replacing it. Use the actual reference for another version. Then verify that document-parser.image in docker-compose.yml uses the chosen registry; a successful sed exit alone does not prove a replacement occurred.

The installer pulls Docling from the selected registry. To download in advance, run the command below; use linux/arm64 on an ARM64 server:

# Use linux/arm64 on an ARM64 server
docker pull --platform linux/amd64 \
quay.nju.edu.cn/docling-project/docling-serve-cpu@sha256:061d35c03611bc15b73d024c8e8387bcf0624279f8b57c16c1567326f214ba56

If a later deployment template specifies another digest, use that value. The CLI obtains temporary credentials, verifies signatures, and pulls Soln product images using the signed release manifest.

Confirm Encryption and Run the Installer​

Confirm the encrypted volume is still mounted. Edit .env.example before the first installation if .env does not exist. On retry, the CLI reads the existing .env; edit that file without overwriting generated secrets. The following prints only four non-secret settings:

findmnt /srv/soln/data
SOLN_INSTALL_ENV=/opt/soln-tenant-runtime/.env.example
if [ -f /opt/soln-tenant-runtime/.env ]; then
SOLN_INSTALL_ENV=/opt/soln-tenant-runtime/.env
fi
grep -E '^(SOLN_DATA_ROOT|SOLN_DATA_AT_REST_ENCRYPTED|RUNTIME_ORIGIN|RUNTIME_BIND_PORT)=' \
"$SOLN_INSTALL_ENV"

If SOLN_DATA_ROOT=/srv/soln/data matches the mount and the directory is really on encryption, set the confirmation value and install:

(
set -e
mountpoint -q /srv/soln/data
test -f "$SOLN_INSTALL_ENV"
sed -i 's/^SOLN_DATA_AT_REST_ENCRYPTED=.*/SOLN_DATA_AT_REST_ENCRYPTED=true/' \
"$SOLN_INSTALL_ENV"
sl install --dir /opt/soln-tenant-runtime \
--setup-file /opt/soln-tenant-runtime/.soln-install-setup.json --yes
)

Installation checks the host, verifies signatures, pulls images, migrates the database, starts services, and waits for readiness. Public dependency images may also be pulled from Docker Hub or Quay. For mainland China and official download addresses, see Deployment Images: Container Images; configure the Docling source after deployment prepare and before installation. If a registry connection times out, fix access to that registry and rerun the same sl install; do not re-register. For offline transfer, use the server's linux/amd64 or linux/arm64 architecture, verify the archive checksum, and use docker load with the same image names and tags.

While waiting, use a second SSH terminal to run docker ps -a --format 'table {{.Names}}\t{{.Status}}'. Migration, MinIO initialization, and sticker seeding containers may finish with Exited (0); long-running services should remain running. Do not start a second installer or remove containers merely because output is quiet. If the CLI has no stage output, record sl version before comparing behavior with another version.

5. Verify and clean up​

After Soln Tenant Runtime ... is ready:

sl status --dir /opt/soln-tenant-runtime --check --json
curl -fsS -o /dev/null -w '%{http_code}\n' \
https://runtime.example.com/health/ready
docker ps --format 'table {{.Names}}\t{{.Status}}'

Expect runtimeReady: true, HTTP 200, and running product services. Then remove the one-time setup file and log out of deployment:

rm /opt/soln-tenant-runtime/.soln-install-setup.json
sl deployment logout

Keep .env and .soln/deployment.json. The pre-upgrade database backup lives under <deployment directory>/.soln/backups and is encrypted by the CLI, but it is outside the /srv/soln/data encrypted mount; protect backups and keys under your storage policy. Do not expose PostgreSQL, MinIO, Temporal, or other internal ports publicly. Complete the restart check below: cloud-native encrypted disks should mount automatically; the manual LUKS route requires unlocking before Docker starts.

6. Recover After a Server Restart​

After installation, arrange a restart during a maintenance window and reconnect over SSH in a root shell. Follow the branch matching your storage route. Do not run luksFormat or mkfs during recovery.

Route A: Verify Automatic Mounting​

Replace the UUID with the one recorded in Route A. Verify the exact mount, Docker and containers without manually starting them:

(
set -e
SOLN_EXPECTED_DATA_UUID='REPLACE_WITH_RECORDED_FILESYSTEM_UUID'
mountpoint -q /srv/soln/data
findmnt -o TARGET,SOURCE,FSTYPE,UUID --mountpoint /srv/soln/data
test "$(findmnt -n -o UUID --mountpoint /srv/soln/data)" = "$SOLN_EXPECTED_DATA_UUID"
systemctl is-active docker.service
docker ps --format 'table {{.Names}}\t{{.Status}}'
)

Long-running generated services use restart: unless-stopped; services manually stopped before reboot stay stopped. One-time initialization containers may remain exited successfully. Allow startup health checks to finish, then run sl status --dir /opt/soln-tenant-runtime --check --json and check https://runtime.example.com/health/ready for HTTP 200. A brief 502 while services start does not establish failure; a persistent failure requires service diagnostics.

If automatic mounting failed, inspect the cloud disk attachment/key permissions, /etc/fstab and journalctl -u srv-soln-data.mount -b --no-pager. After correcting the cause, restore the configured mount and verify the UUID before starting Docker:

(
set -e
SOLN_EXPECTED_DATA_UUID='REPLACE_WITH_RECORDED_FILESYSTEM_UUID'
systemctl daemon-reload
systemctl reset-failed srv-soln-data.mount
if ! mountpoint -q /srv/soln/data; then
mount /srv/soln/data
fi
test "$(findmnt -n -o UUID --mountpoint /srv/soln/data)" = "$SOLN_EXPECTED_DATA_UUID"
systemctl reset-failed docker.service docker.socket
systemctl start docker.service
docker version --format '{{.Server.Version}}'
)

This is recovery from a fault; after fixing it, repeat the restart verification to confirm unattended recovery.

Route B: Unlock and Mount LUKS​

For a LUKS data disk, use the device path recorded during installation; for a file container, use /var/lib/soln/tenant-data.luks. For an existing volume, use the administrator's path and mapping name. If an external key service is configured, verify its unlock and mount units first instead of using the manual passphrase steps.

SOLN_DATA_SOURCE='/REPLACE_WITH_RECORDED_DEVICE_OR_LUKS_FILE_PATH'

If Docker reports Cannot connect to the Docker daemon, check findmnt /srv/soln/data first. An unmet AssertPathIsMountPoint means the mount protection stopped Docker; repeated queries can also cause docker.socket to report trigger-limit-hit. Keep the protection and restore the mount and clear failed unit state below.

Restore the volume and Docker first. Stop on failure, enter the passphrase interactively, and skip operations for existing mappings or mounts:

(
set -e
test -e "$SOLN_DATA_SOURCE"
if ! cryptsetup status soln-tenant-data >/dev/null 2>&1; then
cryptsetup open "$SOLN_DATA_SOURCE" soln-tenant-data
fi
if ! mountpoint -q /srv/soln/data; then
mount /dev/mapper/soln-tenant-data /srv/soln/data
fi
findmnt /srv/soln/data
test "$(findmnt -n -o SOURCE --target /srv/soln/data)" = /dev/mapper/soln-tenant-data
systemctl daemon-reload
systemctl reset-failed docker.service docker.socket
systemctl start docker.socket docker.service
docker version --format '{{.Server.Version}}'
)

Success: the mount source is /dev/mapper/soln-tenant-data and the last command prints the Docker server version.

Verify or Restore the Tenant Services​

Continue here after either storage route. If automatic container recovery succeeded, only run the status and HTTP checks; use the Compose command below when services need to be started again.

Installation not yet completed: retain the setup authorization file and rerun the original sl install. Do not use the installed-deployment recovery commands below, re-register, or format storage.

Installed deployment: read the actual release override from deployment state rather than starting the template's default product images:

(
set -e
SOLN_RELEASE_OVERRIDE=$(node -e '
const fs = require("node:fs");
const state = JSON.parse(fs.readFileSync("/opt/soln-tenant-runtime/.soln/deployment.json", "utf8"));
if (typeof state.releaseOverrideFile !== "string" || !state.releaseOverrideFile) process.exit(1);
process.stdout.write(state.releaseOverrideFile);
')
test -f "$SOLN_RELEASE_OVERRIDE"
docker compose --project-directory /opt/soln-tenant-runtime \
--env-file /opt/soln-tenant-runtime/.env \
-f /opt/soln-tenant-runtime/docker-compose.yml \
-f "$SOLN_RELEASE_OVERRIDE" up -d
sl status --dir /opt/soln-tenant-runtime --check --json
curl -fsS -o /dev/null -w '%{http_code}\n' \
https://runtime.example.com/health/ready
)

Confirm runtimeReady: true and public HTTP 200 before using the tenant. Dependencies may need time to become healthy after startup; inspect the failing service if necessary. Adjust all paths and mapping names to match your installation. If Docker still cannot start, run systemctl status docker.service docker.socket --no-pager -l and journalctl -u docker.service -n 80 --no-pager; do not reinstall the tenant.

Next: Upgrade and Rollback.

To change an installed Runtime’s public address, see Change the Tenant Domain.