Recommend systemd user lingering

This commit is contained in:
Federico Jaramillo Martinez
2026-05-09 20:20:44 +02:00
parent c13c7072e3
commit fc24e06324
2 changed files with 51 additions and 1 deletions
+19
View File
@@ -92,9 +92,15 @@ Recommended install uses npm plus systemd user services:
```bash
npm install -g @jmfederico/pi-web
# Recommended on servers: keep user services running after logout/reboot.
sudo loginctl enable-linger "$USER"
pi-web install
```
`loginctl enable-linger` is optional for local desktop use, but recommended on servers. It lets the user systemd manager start at boot and continue running after you log out, so Pi Web remains available without an active SSH/login session.
This writes and starts:
- `~/.config/systemd/user/pi-web-sessiond.service`
@@ -102,6 +108,12 @@ This writes and starts:
The generated services run through `bash -lc` so they see a shell environment similar to running `pi` from your terminal.
To check whether lingering is enabled:
```bash
loginctl show-user "$USER" -p Linger
```
Open <http://127.0.0.1:8504>.
Useful commands:
@@ -236,6 +248,13 @@ Restart=no
WantedBy=default.target
```
On servers, enable persistent user services so the user systemd manager starts at boot and remains running after logout:
```bash
sudo loginctl enable-linger "$USER"
loginctl show-user "$USER" -p Linger
```
After creating or changing units:
```bash
+32 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env node
import { existsSync } from "node:fs";
import { mkdir, rm, writeFile } from "node:fs/promises";
import { homedir } from "node:os";
import { homedir, userInfo } from "node:os";
import { dirname, join, resolve } from "node:path";
import { spawnSync } from "node:child_process";
import { defaultPiWebConfigPath, examplePiWebConfig } from "./config.js";
@@ -32,6 +32,16 @@ function hasCommand(command: string): boolean {
return capture("/usr/bin/env", ["bash", "-lc", `command -v ${command}`]).status === 0;
}
function isLingerEnabled(): boolean | undefined {
if (!hasCommand("loginctl")) return undefined;
const result = capture("loginctl", ["show-user", userInfo().username, "-p", "Linger"]);
if (result.status !== 0) return undefined;
const value = result.stdout.trim();
if (value === "Linger=yes") return true;
if (value === "Linger=no") return false;
return undefined;
}
function parseInstallOptions(args: string[]): InstallOptions {
const options: InstallOptions = { host: "127.0.0.1", port: "8504" };
for (let i = 0; i < args.length; i += 1) {
@@ -136,6 +146,16 @@ async function install(args: string[]): Promise<void> {
console.log(`\nPi Web is installed and starting.`);
console.log(`Config: ${configPath}`);
console.log(`Open: http://${options.host === "0.0.0.0" ? "127.0.0.1" : options.host}:${options.port}`);
const linger = isLingerEnabled();
if (linger === false) {
console.log("\nRecommended for server use: keep user services running after logout/reboot:");
console.log(` sudo loginctl enable-linger ${userInfo().username}`);
} else if (linger === undefined) {
console.log("\nRecommended for server use: enable systemd user lingering so services survive logout/reboot:");
console.log(` sudo loginctl enable-linger ${userInfo().username}`);
}
console.log("\nUseful commands:");
console.log(" pi-web status");
console.log(" pi-web logs");
@@ -181,6 +201,17 @@ function doctor(): void {
if (output !== "") console.log(` ${output.split("\n")[0] ?? ""}`);
}
const linger = isLingerEnabled();
if (linger === true) {
console.log("✓ systemd user lingering enabled");
} else if (linger === false) {
console.log("✗ systemd user lingering disabled");
console.log(` Recommended on servers: sudo loginctl enable-linger ${userInfo().username}`);
} else {
console.log("? systemd user lingering unknown");
console.log(` Recommended on servers: sudo loginctl enable-linger ${userInfo().username}`);
}
if (failed) {
console.log("\nIf a command works in your terminal but fails here, make sure your bash login files set PATH the same way.");
process.exitCode = 1;