- syncup: podman first, docker only as fallback (podman wins on name clash;
existing docker units found in podman switch to podman)
- syncup: group podman-compose containers into one `kind: stack` unit per
project (project, working_dir, compose_files, compose_bin from labels);
disable existing member units ("part of stack <project>")
- generator: compose mode (podman-compose up/stop, WorkingDirectory) with
pod start/stop fallback when compose binary/dir/files are missing
- reconcile: stacks disabled with "stack not found" when neither compose
files nor pod exist
- root: scan root + rootless users listed in `users:` (podman only)
- non-root: config ~/.config/unitdore/units.yaml, own user units only
- sysuser package: runuser with XDG_RUNTIME_DIR/DBUS env, home/linger lookup
- install: warn when linger is not enabled for user units
- testdata/stack: podman-compose test stack and captured podman ps fixture
- docs: README, generated-units, man page, AGENTS, PLAN
6.7 KiB
Generated Systemd Unit Files
Unitdore generates .service files from your units.yaml config.
This document shows every combination with annotated examples.
System Unit (root, no user)
Config:
units:
- name: nginx
runtime: podman
order: 1
enabled: true
Written to: /etc/systemd/system/unitdore-nginx.service
# /etc/systemd/system/unitdore-nginx.service
# Generated by unitdore — do not edit manually
[Unit]
Description=Unitdore: nginx (podman)
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/podman start -a nginx
ExecStop=/usr/bin/podman stop nginx
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Notes:
After=network.targetensures the network is up before startingWantedBy=multi-user.targetmeans it starts in normal boot runlevelRestart=on-failurerestarts the container if it exits unexpectedly
System Unit (Docker)
Config:
units:
- name: redis
runtime: docker
order: 1
enabled: true
Written to: /etc/systemd/system/unitdore-redis.service
# /etc/systemd/system/unitdore-redis.service
# Generated by unitdore — do not edit manually
[Unit]
Description=Unitdore: redis (docker)
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/docker start redis
ExecStop=/usr/bin/docker stop redis
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Notes:
- Docker uses
docker start <name>(no-aflag needed)
User Unit (rootless container)
Config:
units:
- name: myapp
runtime: podman
user: hein
order: 2
enabled: true
Written to: /home/hein/.config/systemd/user/unitdore-myapp.service
# ~/.config/systemd/user/unitdore-myapp.service
# Generated by unitdore — do not edit manually
[Unit]
Description=Unitdore: myapp (podman)
After=default.target
[Service]
Type=simple
ExecStart=/usr/bin/podman start -a myapp
ExecStop=/usr/bin/podman stop myapp
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
Notes:
- User units use
After=default.targetandWantedBy=default.target - The file is written to the user's own systemd directory
- Unitdore calls
runuser -u hein -- systemctl --userfor enable/start/status
Custom Command
Config:
units:
- name: myscript
runtime: podman
command: /opt/myapp/start.sh
order: 3
enabled: true
Written to: /etc/systemd/system/unitdore-myscript.service
# /etc/systemd/system/unitdore-myscript.service
# Generated by unitdore — do not edit manually
[Unit]
Description=Unitdore: myscript (podman)
After=network.target
[Service]
Type=simple
ExecStart=/opt/myapp/start.sh
ExecStop=
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Notes:
- When
commandis set, it is used verbatim asExecStart ExecStopis left empty — systemd will send SIGTERM to the process group
With Prefix and Suffix
Config:
prefix: prod-
suffix: -svc
units:
- name: nginx
runtime: podman
order: 1
enabled: true
- name: api
runtime: docker
order: 2
enabled: true
Written to:
/etc/systemd/system/unitdore-prod-nginx-svc.service/etc/systemd/system/unitdore-prod-api-svc.service
# /etc/systemd/system/unitdore-prod-nginx-svc.service
# Generated by unitdore — do not edit manually
[Unit]
Description=Unitdore: nginx (podman)
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/podman start -a nginx
ExecStop=/usr/bin/podman stop nginx
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Notes:
prefixandsuffixapply to the service file name only — the container name stays unchanged- Useful for distinguishing environments (
prod-,staging-) or roles (-web,-worker)
Container Named by Short ID
If a container has no name (e.g. started with docker run without --name), unitdore uses its short container ID (first 12 characters) as the unit name.
Example: Container ID a3f2c1b9e4d7 with no name →
Config entry added by syncup:
units:
- name: a3f2c1b9e4d7
runtime: docker
order: 1
enabled: true
Written to: /etc/systemd/system/unitdore-a3f2c1b9e4d7.service
Tip: After syncup, use unitdore edit to give it a proper name before running install.
Startup Ordering Example
Config:
units:
- name: postgres
runtime: podman
order: 1
enabled: true
- name: redis
runtime: podman
order: 1 # same order = starts in parallel
enabled: true
- name: api
runtime: podman
order: 2 # waits until order 1 group is started
enabled: true
- name: nginx
runtime: podman
order: 3
enabled: true
unitdore active starts units in ascending order value, lowest first.
Units with the same order are started together (parallel).
Disabled Unit
Config:
units:
- name: oldthing
runtime: podman
order: 1
enabled: false
disabled_reason: container not found
unitdore installwill remove the.servicefile if it existsunitdore activewill skip disabled unitsunitdore statusshows the reason in the REASON column
Stack Unit (podman-compose, compose mode)
Used when compose_bin, working_dir and all compose_files exist.
- name: shop
kind: stack
runtime: podman
project: shop
working_dir: /srv/shop
compose_files: [compose.yaml]
compose_bin: /usr/bin/podman-compose
enabled: true
# /etc/systemd/system/unitdore-shop.service
# Generated by unitdore — do not edit manually
[Unit]
Description=Unitdore: shop (podman-compose stack)
After=network.target
[Service]
Type=simple
WorkingDirectory=/srv/shop
ExecStart=/usr/bin/podman-compose -p shop -f compose.yaml up
ExecStop=-/usr/bin/podman-compose -p shop -f compose.yaml stop
[Install]
WantedBy=multi-user.target
Stack Unit (pod fallback)
Used when the compose binary, working dir or a compose file is missing. Requires pod pod_<project>.
[Unit]
Description=Unitdore: shop (podman pod)
After=network.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/podman pod start pod_shop
ExecStop=-/usr/bin/podman pod stop pod_shop
Quick Reference: Service File Locations
| Scenario | Path |
|---|---|
| System unit (root) | /etc/systemd/system/unitdore-<name>.service |
| User unit | /home/<user>/.config/systemd/user/unitdore-<name>.service |
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 |