feat: OpenRC and runit init backends behind an Init interface
Extract init-neutral unit resolution into service/, add initsys.Init with systemd, openrc and runit backends, selected by 'init:' config or autodetect. OpenRC and runit are root only; rootless units run as system services as that user. Add docs and generated examples for all three.
This commit is contained in:
+323
-1
@@ -1,4 +1,4 @@
|
||||
# Generated Systemd Unit Files
|
||||
# Generated Unit Files (systemd, OpenRC, runit)
|
||||
|
||||
Unitdore generates `.service` files from your `units.yaml` config.
|
||||
This document shows every combination with annotated examples.
|
||||
@@ -330,6 +330,326 @@ ExecStop=-/usr/bin/podman pod stop pod_shop
|
||||
|
||||
---
|
||||
|
||||
## Config Behind the Init System Examples
|
||||
|
||||
```yaml
|
||||
init: systemd # systemd | openrc | runit
|
||||
units:
|
||||
- {name: nginx, runtime: podman, order: 1, enabled: true}
|
||||
- {name: redis, runtime: docker, order: 2, enabled: true, restart: true, restart_sec: 5, restart_retries: 3}
|
||||
- {name: myapp, runtime: podman, user: alice, order: 3, enabled: true}
|
||||
- {name: backup, runtime: script, script: /opt/scripts/backup.sh, stop_script: /opt/scripts/stop.sh, shell: /bin/sh, working_dir: /opt/scripts, order: 4, enabled: true}
|
||||
- {name: agent, runtime: exec, executable: /opt/agent/bin/agent, args: [--config, /etc/agent.toml], service_type: oneshot, order: 5, enabled: true}
|
||||
- {name: shop, runtime: podman, kind: stack, project: shop, order: 6, enabled: true}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Script and Executable Units (systemd)
|
||||
|
||||
`script` runs `<shell> <script>` (default shell `/bin/bash`); `exec` runs the binary directly.
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/unitdore-backup.service
|
||||
# Generated by unitdore — do not edit manually
|
||||
|
||||
[Unit]
|
||||
Description=Unitdore: backup (sh script)
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/scripts
|
||||
ExecStart=/bin/sh /opt/scripts/backup.sh
|
||||
ExecStop=-/bin/sh /opt/scripts/stop.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/unitdore-agent.service
|
||||
# Generated by unitdore — do not edit manually
|
||||
|
||||
[Unit]
|
||||
Description=Unitdore: agent (executable)
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/opt/agent/bin/agent --config /etc/agent.toml
|
||||
ExecStop=
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OpenRC
|
||||
|
||||
Root only. Files in `/etc/init.d/`. Rootless `user:` units run as a system service via `command_user`.
|
||||
|
||||
### nginx
|
||||
|
||||
```sh
|
||||
#!/sbin/openrc-run
|
||||
# /etc/init.d/unitdore-nginx
|
||||
# Generated by unitdore — do not edit manually
|
||||
|
||||
description="Unitdore: nginx (podman)"
|
||||
|
||||
command_background=yes
|
||||
pidfile="/run/${RC_SVCNAME}.pid"
|
||||
command="/usr/bin/podman"
|
||||
command_args="start -a nginx"
|
||||
|
||||
depend() {
|
||||
need net
|
||||
}
|
||||
|
||||
stop_pre() {
|
||||
/usr/bin/podman stop nginx || true
|
||||
}
|
||||
```
|
||||
|
||||
### redis
|
||||
|
||||
```sh
|
||||
#!/sbin/openrc-run
|
||||
# /etc/init.d/unitdore-redis
|
||||
# Generated by unitdore — do not edit manually
|
||||
|
||||
description="Unitdore: redis (docker)"
|
||||
|
||||
supervisor=supervise-daemon
|
||||
respawn_delay=5
|
||||
respawn_max=3
|
||||
command="/usr/bin/docker"
|
||||
command_args="start -a redis"
|
||||
|
||||
depend() {
|
||||
need net
|
||||
need docker
|
||||
}
|
||||
|
||||
stop_pre() {
|
||||
/usr/bin/docker stop redis || true
|
||||
}
|
||||
```
|
||||
|
||||
### myapp
|
||||
|
||||
```sh
|
||||
#!/sbin/openrc-run
|
||||
# /etc/init.d/unitdore-myapp
|
||||
# Generated by unitdore — do not edit manually
|
||||
|
||||
description="Unitdore: myapp (podman)"
|
||||
export HOME=/home/alice
|
||||
export XDG_RUNTIME_DIR=/run/user/1000
|
||||
|
||||
command_background=yes
|
||||
pidfile="/run/${RC_SVCNAME}.pid"
|
||||
command="/usr/bin/podman"
|
||||
command_args="start -a myapp"
|
||||
command_user="alice"
|
||||
|
||||
depend() {
|
||||
need net
|
||||
}
|
||||
|
||||
start_pre() {
|
||||
checkpath -d -m 0700 -o alice /run/user/1000
|
||||
}
|
||||
|
||||
stop_pre() {
|
||||
su -m -s /bin/sh -c '/usr/bin/podman stop myapp' alice || true
|
||||
}
|
||||
```
|
||||
|
||||
### backup
|
||||
|
||||
```sh
|
||||
#!/sbin/openrc-run
|
||||
# /etc/init.d/unitdore-backup
|
||||
# Generated by unitdore — do not edit manually
|
||||
|
||||
description="Unitdore: backup (sh script)"
|
||||
|
||||
command_background=yes
|
||||
pidfile="/run/${RC_SVCNAME}.pid"
|
||||
command="/bin/sh"
|
||||
command_args="/opt/scripts/backup.sh"
|
||||
directory="/opt/scripts"
|
||||
|
||||
depend() {
|
||||
need net
|
||||
}
|
||||
|
||||
stop_pre() {
|
||||
/bin/sh /opt/scripts/stop.sh || true
|
||||
}
|
||||
```
|
||||
|
||||
### agent
|
||||
|
||||
```sh
|
||||
#!/sbin/openrc-run
|
||||
# /etc/init.d/unitdore-agent
|
||||
# Generated by unitdore — do not edit manually
|
||||
|
||||
description="Unitdore: agent (executable)"
|
||||
|
||||
start() {
|
||||
ebegin "Starting $RC_SVCNAME"
|
||||
/opt/agent/bin/agent --config /etc/agent.toml
|
||||
eend $?
|
||||
}
|
||||
|
||||
depend() {
|
||||
need net
|
||||
}
|
||||
```
|
||||
|
||||
### shop
|
||||
|
||||
```sh
|
||||
#!/sbin/openrc-run
|
||||
# /etc/init.d/unitdore-shop
|
||||
# Generated by unitdore — do not edit manually
|
||||
|
||||
description="Unitdore: shop (podman pod)"
|
||||
|
||||
start() {
|
||||
ebegin "Starting $RC_SVCNAME"
|
||||
/usr/bin/podman pod start pod_shop
|
||||
eend $?
|
||||
}
|
||||
|
||||
stop() {
|
||||
ebegin "Stopping $RC_SVCNAME"
|
||||
/usr/bin/podman pod stop pod_shop
|
||||
eend $?
|
||||
}
|
||||
|
||||
depend() {
|
||||
need net
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## runit
|
||||
|
||||
Root only. Each unit is `/etc/sv/unitdore-<name>/{run,finish}` symlinked into the service dir. Install adds a `down` file (no autostart until started/enabled). Both files are shown per unit.
|
||||
|
||||
### nginx
|
||||
|
||||
```sh
|
||||
# ===== /etc/sv/unitdore-nginx/run =====
|
||||
#!/bin/sh
|
||||
# unitdore-nginx — generated by unitdore, do not edit manually
|
||||
exec 2>&1
|
||||
exec /usr/bin/podman start -a nginx
|
||||
|
||||
# ===== /etc/sv/unitdore-nginx/finish =====
|
||||
#!/bin/sh
|
||||
# unitdore-nginx — generated by unitdore, do not edit manually
|
||||
/usr/bin/podman stop nginx || true
|
||||
exec sv down .
|
||||
```
|
||||
|
||||
### redis
|
||||
|
||||
```sh
|
||||
# ===== /etc/sv/unitdore-redis/run =====
|
||||
#!/bin/sh
|
||||
# unitdore-redis — generated by unitdore, do not edit manually
|
||||
exec 2>&1
|
||||
exec /usr/bin/docker start -a redis
|
||||
|
||||
# ===== /etc/sv/unitdore-redis/finish =====
|
||||
#!/bin/sh
|
||||
# unitdore-redis — generated by unitdore, do not edit manually
|
||||
/usr/bin/docker stop redis || true
|
||||
[ "$1" = 0 ] && exec sv down .
|
||||
sleep 5
|
||||
```
|
||||
|
||||
### myapp
|
||||
|
||||
```sh
|
||||
# ===== /etc/sv/unitdore-myapp/run =====
|
||||
#!/bin/sh
|
||||
# unitdore-myapp — generated by unitdore, do not edit manually
|
||||
exec 2>&1
|
||||
[ -d /run/user/1000 ] || install -d -m 0700 -o alice /run/user/1000
|
||||
export HOME=/home/alice
|
||||
export XDG_RUNTIME_DIR=/run/user/1000
|
||||
exec chpst -u alice /usr/bin/podman start -a myapp
|
||||
|
||||
# ===== /etc/sv/unitdore-myapp/finish =====
|
||||
#!/bin/sh
|
||||
# unitdore-myapp — generated by unitdore, do not edit manually
|
||||
chpst -u alice /usr/bin/podman stop myapp || true
|
||||
exec sv down .
|
||||
```
|
||||
|
||||
### backup
|
||||
|
||||
```sh
|
||||
# ===== /etc/sv/unitdore-backup/run =====
|
||||
#!/bin/sh
|
||||
# unitdore-backup — generated by unitdore, do not edit manually
|
||||
exec 2>&1
|
||||
cd /opt/scripts || exit 1
|
||||
exec /bin/sh /opt/scripts/backup.sh
|
||||
|
||||
# ===== /etc/sv/unitdore-backup/finish =====
|
||||
#!/bin/sh
|
||||
# unitdore-backup — generated by unitdore, do not edit manually
|
||||
cd /opt/scripts 2>/dev/null
|
||||
/bin/sh /opt/scripts/stop.sh || true
|
||||
exec sv down .
|
||||
```
|
||||
|
||||
### agent
|
||||
|
||||
```sh
|
||||
# ===== /etc/sv/unitdore-agent/run =====
|
||||
#!/bin/sh
|
||||
# unitdore-agent — generated by unitdore, do not edit manually
|
||||
exec 2>&1
|
||||
/opt/agent/bin/agent --config /etc/agent.toml || exit 1
|
||||
exec tail -f /dev/null
|
||||
|
||||
# ===== /etc/sv/unitdore-agent/finish =====
|
||||
#!/bin/sh
|
||||
# unitdore-agent — generated by unitdore, do not edit manually
|
||||
exec sv down .
|
||||
```
|
||||
|
||||
### shop
|
||||
|
||||
```sh
|
||||
# ===== /etc/sv/unitdore-shop/run =====
|
||||
#!/bin/sh
|
||||
# unitdore-shop — generated by unitdore, do not edit manually
|
||||
exec 2>&1
|
||||
/usr/bin/podman pod start pod_shop || exit 1
|
||||
exec tail -f /dev/null
|
||||
|
||||
# ===== /etc/sv/unitdore-shop/finish =====
|
||||
#!/bin/sh
|
||||
# unitdore-shop — generated by unitdore, do not edit manually
|
||||
/usr/bin/podman pod stop pod_shop || true
|
||||
exec sv down .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference: Service File Locations
|
||||
|
||||
| Scenario | Path |
|
||||
@@ -339,3 +659,5 @@ ExecStop=-/usr/bin/podman pod stop pod_shop
|
||||
| With prefix `prod-` | `/etc/systemd/system/unitdore-prod-<name>.service` |
|
||||
| With suffix `-svc` | `/etc/systemd/system/unitdore-<name>-svc.service` |
|
||||
| With both | `/etc/systemd/system/unitdore-prod-<name>-svc.service` |
|
||||
| OpenRC | `/etc/init.d/unitdore-<name>` |
|
||||
| runit | `/etc/sv/unitdore-<name>/` (+ symlink in `/var/service` or `/etc/service`) |
|
||||
|
||||
+9
-2
@@ -1,6 +1,6 @@
|
||||
.TH UNITDORE 1 "2026-04-08" "0.1.0" "User Commands"
|
||||
.SH NAME
|
||||
unitdore \- manage container units via systemd
|
||||
unitdore \- manage container units via systemd, OpenRC or runit
|
||||
.SH SYNOPSIS
|
||||
.B unitdore
|
||||
[\fB\-\-config\fR \fIpath\fR]
|
||||
@@ -106,6 +106,13 @@ units:
|
||||
Stack units run \fBpodman\-compose up\fR/\fBstop\fR when compose_bin,
|
||||
working_dir and compose_files exist; otherwise \fBpodman pod start\fR/\fBstop
|
||||
pod_\fR\fIproject\fR.
|
||||
.SH INIT SYSTEMS
|
||||
The \fBinit:\fR config key selects \fBsystemd\fR, \fBopenrc\fR or \fBrunit\fR.
|
||||
Empty means autodetect, falling back to systemd. OpenRC and runit are root only;
|
||||
rootless units run as system services as that user. runit service links go in
|
||||
\fBservice_dir:\fR (default \fI/var/service\fR or \fI/etc/service\fR).
|
||||
OpenRC scripts are written to \fI/etc/init.d/unitdore\-*\fR and runit services to
|
||||
\fI/etc/sv/unitdore\-*\fR.
|
||||
.SH GENERATED SERVICE FILES
|
||||
System units are written to \fI/etc/systemd/system/\fR and user units to
|
||||
\fI~/.config/systemd/user/\fR.
|
||||
@@ -156,7 +163,7 @@ Generated system unit files.
|
||||
.I ~/.config/systemd/user/unitdore-*.service
|
||||
Generated rootless user unit files.
|
||||
.SH REQUIREMENTS
|
||||
systemd; Podman and/or Docker (only installed runtimes are used);
|
||||
systemd, OpenRC or runit; Podman and/or Docker (only installed runtimes are used);
|
||||
podman\-compose for compose\-mode stacks.
|
||||
User units need linger to start at boot: \fBloginctl enable\-linger\fR \fIuser\fR.
|
||||
Root is required for system units; the relevant user for rootless units.
|
||||
|
||||
Reference in New Issue
Block a user