Install on Linux

Install loomctl and the Loomplane server on Linux from a .deb, an .rpm or a release archive, run the server under systemd, then upgrade or remove both.

Every GitHub release carries Linux builds of both programs for amd64 and arm64. loomctl is the command-line client, and you can put it on any machine. The server is loomplane (the API and dashboard) plus loomplane-worker (repository analysis), and it wants a host of its own with PostgreSQL. This page runs the server straight on the host under systemd. If you'd rather keep it in containers, Production on Docker covers that setup.

What a release ships for Linux

For a release tagged v1.2.3:

FileWhat it holds
loomctl_1.2.3_amd64.deb, loomctl_1.2.3_arm64.debloomctl in /usr/bin, for Debian and Ubuntu
loomctl-1.2.3-1.x86_64.rpm, loomctl-1.2.3-1.aarch64.rpmThe same for Fedora, RHEL and their relatives
loomctl_1.2.3_linux_amd64.tar.gz, loomctl_1.2.3_linux_arm64.tar.gzThe bare binary, for any distribution
loomplane_1.2.3_amd64.deb, loomplane_1.2.3_arm64.debThe server: both binaries, the dashboard, config.yaml, a settings file and two systemd units
loomplane-1.2.3-1.x86_64.rpm, loomplane-1.2.3-1.aarch64.rpmThe same server package for dnf
loomplane_1.2.3_linux_amd64.tar.gz, loomplane_1.2.3_linux_arm64.tar.gzThe server as an archive, with the same units under systemd/
binaries-checksums.txtSHA-256 of every file above
binaries-checksums.txt.sigstore.jsonA Sigstore signature over that checksum file, made by the Release workflow

There's also a loomctl container image, ghcr.io/xraph/loomctl, for linux/amd64 and linux/arm64.

Download and verify

Pick the release and your architecture first. Every command below reuses these two variables:

VERSION=$(curl -fsSL https://api.github.com/repos/xraph/loomplane/releases/latest \
  | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
case "$(uname -m)" in
  x86_64) ARCH=amd64 ;;
  aarch64 | arm64) ARCH=arm64 ;;
  *) echo "Loomplane ships Linux builds for amd64 and arm64 only" >&2 ;;
esac
echo "$VERSION $ARCH"

That asks GitHub for the newest stable release. For a host you'll keep, set VERSION=1.2.3 yourself so you know what you installed. Then fetch the checksums and their signature next to whatever you download:

BASE=https://github.com/xraph/loomplane/releases/download/v$VERSION
curl -fsSLO "$BASE/binaries-checksums.txt"
curl -fsSLO "$BASE/binaries-checksums.txt.sigstore.json"

Once the files you want are in the same directory, check them. --ignore-missing skips everything in the list you didn't download:

sha256sum --check --ignore-missing binaries-checksums.txt

The checksum file proves the download wasn't damaged. To prove the Release workflow in xraph/loomplane made it, verify its signature with cosign:

cosign verify-blob \
  --bundle binaries-checksums.txt.sigstore.json \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --certificate-identity-regexp '^https://github\.com/xraph/loomplane/\.github/workflows/release\.yml@refs/(tags/v.+|heads/main)$' \
  binaries-checksums.txt

It should print Verified OK. A release cut from the Actions tab is signed from main and one cut from a tag push from that tag, which is why the identity allows both. Once you have loomctl, loomctl node verify-release runs the same check without cosign.

Install loomctl

curl -fsSLO "$BASE/loomctl_${VERSION}_${ARCH}.deb"
sha256sum --check --ignore-missing binaries-checksums.txt
sudo apt install "./loomctl_${VERSION}_${ARCH}.deb"

Check it, then sign in. --no-browser prints the sign-in address instead of opening it, which is what you want over SSH:

loomctl --version
loomctl login --url https://loomplane.example.com --no-browser

Open that address on your laptop or phone, check the code matches, and approve it. CI signs in with an API key instead, which CI with loomctl covers, and the loomctl reference lists every command.

Install the server

What you need

  • A Linux host on amd64 or arm64 with systemd 239 or newer. Ubuntu 20.04, Debian 11, RHEL 8 and anything newer qualify.
  • PostgreSQL 17, on this host or reachable from it. A managed database works fine.
  • A hostname pointed at the host, with TCP ports 80 and 443 open for HTTPS.
  • 2 CPU cores and 8 GB of RAM to start, plus disk for the database, deployment sources and checkouts the worker unpacks (up to 2 GiB per analysis). Measure your own workload before you go smaller.

Loomplane never runs your code, so the host needs no Docker, no Git and no build tools.

Set up PostgreSQL

Skip this if you already have a database. On Debian or Ubuntu, the PostgreSQL project's own repository has version 17 for every supported release:

sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y
sudo apt install -y postgresql-17

On Fedora or RHEL, follow the PostgreSQL download page for your release. Then make a role and a database for Loomplane. A hex password needs no escaping in a URL:

DB_PASSWORD=$(openssl rand -hex 32)
sudo -u postgres psql -v ON_ERROR_STOP=1 \
  -c "CREATE ROLE loomplane LOGIN PASSWORD '$DB_PASSWORD'" \
  -c "CREATE DATABASE loomplane OWNER loomplane"
echo "postgres://loomplane:$DB_PASSWORD@127.0.0.1:5432/loomplane?sslmode=disable"

Keep that last line. It's your LOOMPLANE_DATABASE_URL. Use sslmode=require or stricter for a database on another machine.

Install the package

curl -fsSLO "$BASE/loomplane_${VERSION}_${ARCH}.deb"
sha256sum --check --ignore-missing binaries-checksums.txt
sudo apt install "./loomplane_${VERSION}_${ARCH}.deb"

Either way, the files land here:

Path
/usr/bin/loomplane, /usr/bin/loomplane-workerThe binaries (/usr/local/bin from the archive)
/usr/share/loomplane/dashboardThe built dashboard the API serves
/etc/loomplane/config.yamlDefaults. Leave it alone, since an upgrade replaces it
/etc/loomplane/loomplane.envYour settings and secrets, readable by root and the loomplane group only
loomplane.service, loomplane-worker.serviceThe systemd units, both running as the loomplane user
/var/lib/loomplaneDeployment sources. systemd creates it on first start
/var/lib/loomplane-workThe worker's checkouts

Installing starts nothing. You fill in the settings first.

Fill in the settings

openssl rand -base64 32
sudoedit /etc/loomplane/loomplane.env

Four values are required:

Variable
LOOMPLANE_ENVIRONMENTAlready production. Leave it
LOOMPLANE_PUBLIC_URLhttps:// and your hostname, no path
LOOMPLANE_SECRET_KEYThe value openssl rand -base64 32 printed
LOOMPLANE_DATABASE_URLThe URL from the PostgreSQL step

The rest of the file has optional settings (email, off-host backups, the AI section) with a comment on each. Any LOOMPLANE_ variable from Configuration can go here, and both services read the same file.

LOOMPLANE_SECRET_KEY encrypts every cloud credential and secret Loomplane stores. Generate it once, keep a copy in your secret manager, and keep it out of your backups. If you lose it, you lose every stored credential.

Start it

Start the API first. It applies the database migrations before it serves, and the worker waits for them.

sudo systemctl enable --now loomplane
curl -fsS http://127.0.0.1:8780/_/health/live && echo
sudo systemctl enable --now loomplane-worker
systemctl status loomplane loomplane-worker --no-pager

The first start migrates an empty database, so give it a minute. If curl fails, journalctl -u loomplane -e says why, and a missing setting is named there.

Put HTTPS in front

Both services listen on 127.0.0.1 only. A reverse proxy serves your hostname over HTTPS and passes traffic in. With Caddy, which gets and renews the certificate for you, /etc/caddy/Caddyfile is:

loomplane.example.com {
	# Health and metrics stay on the host.
	@internal path /_/health /_/health/* /_/metrics /_/metrics/* /debug/*
	respond @internal 404

	reverse_proxy 127.0.0.1:8780 {
		# Send events as they happen. WebSocket upgrades work as is.
		flush_interval -1
	}
}
sudo systemctl reload caddy

nginx works too, as long as it passes WebSocket upgrades and turns off buffering for the API's event streams (proxy_buffering off).

Finish setup

Setup needs a one-time link. The API logs it as a setup is waiting warning (journalctl -u loomplane | grep setup), and loomplane setup-token prints it again.

Operator commands like that one need the service's settings and the service's user. They also read a .env from the directory you run them in, so start them from /var/lib/loomplane. This shell function does all three, and the rest of this page uses it:

lp() {
  sudo -u loomplane sh -c 'cd /var/lib/loomplane && set -a && . /etc/loomplane/loomplane.env && set +a && exec loomplane "$@"' loomplane "$@"
}
lp setup-token
# Finish setup at https://loomplane.example.com/setup?token=...

Open that link, create your account and your organization, and connect a repository. The first account owns the instance. Quick start picks up from there.

Run it as a service

The two units behave like any other systemd service:

systemctl status loomplane loomplane-worker
journalctl -u loomplane -u loomplane-worker -f
sudo systemctl restart loomplane

Change a setting by editing /etc/loomplane/loomplane.env and restarting both. systemd restarts either service if it exits with an error, and starts both at boot once you've enabled them.

The units lock things down. Each runs as loomplane with no capabilities, can't gain privileges, sees a read-only filesystem apart from its own directory under /var/lib and a private /tmp, and can't see /home. To change one, use a drop-in instead of editing the unit, so an upgrade doesn't overwrite it:

sudo systemctl edit loomplane

The loomplane binary has operator commands too: migrations, migrate, backup, restore and setup-token. Run them through the lp function from the setup step. Put it in root's shell profile if you'll use it often:

lp migrations
# N applied, 0 pending.

Backing up Loomplane covers backups. With this layout, the files to keep besides the database are /var/lib/loomplane and /etc/loomplane/loomplane.env, and the secret key belongs somewhere other than those backups.

Upgrade

loomctl

Install the newer package over the old one (sudo apt install ./loomctl_...deb or sudo dnf install ./loomctl-...rpm), or replace the binary in /usr/local/bin. Your sign-in stays.

The server

The order matters: back up, stop both services, install, start the API so it migrates, then start the worker. Upgrades and releases explains why, and how to roll back.

VERSION=1.2.4   # the release you're moving to

# Back up with the version you have now, while nothing writes.
sudo systemctl stop loomplane-worker loomplane
lp backup --out /var/lib/loomplane/pre-upgrade.tar.gz
sudo cp /var/lib/loomplane/pre-upgrade.tar.gz /root/

# Download and verify the new release as above, then install it.
sudo apt install "./loomplane_${VERSION}_${ARCH}.deb"   # or dnf install ./loomplane-...rpm

lp migrations   # shows what the new version will apply
sudo systemctl start loomplane
curl -fsS http://127.0.0.1:8780/_/health/live && echo
sudo systemctl start loomplane-worker

Copy that backup off the host too. The package keeps your loomplane.env, and it never starts or restarts the services by itself, so nothing runs the new version until you start it.

From the archive, it's the same order. Stop both, copy the new binaries into /usr/local/bin and the new dashboard into /usr/local/share/loomplane/, replace /etc/loomplane/config.yaml, then start the API and the worker. Compare the new systemd/ files with yours too, since a release can change them.

Loomplane won't start against a database a newer version migrated. Going back means restoring the backup you made before upgrading.

Uninstall

loomctl

loomctl logout
sudo apt remove loomctl        # or: sudo dnf remove loomctl

From the archive, sudo rm /usr/local/bin/loomctl. loomctl logout ends your session on the server and deletes the saved login under ~/.config/loomplane.

The server

Removing the package stops and disables both services. It leaves your data, your settings and the loomplane user, in case you meant to reinstall:

sudo apt remove loomplane      # or: sudo dnf remove loomplane

apt purge loomplane removes /etc/loomplane as well. To remove everything else, once you're sure you have the backup you want:

sudo rm -rf /var/lib/loomplane /var/lib/loomplane-work /etc/loomplane
sudo userdel loomplane
sudo -u postgres psql -c "DROP DATABASE loomplane" -c "DROP ROLE loomplane"

From the archive, stop and disable both services, then delete the two unit files from /etc/systemd/system, the binaries from /usr/local/bin and /usr/local/share/loomplane, and run sudo systemctl daemon-reload before the commands above.

On this page