# unitdore > *A door you open and close for container units.* Unitdore bridges your container runtime (Podman, Docker) and your init system (systemd, OpenRC, runit). It discovers running containers and podman-compose stacks, stores them in a config file, and generates + manages a service for each one. ## Install **Arch Linux (AUR)** ```bash yay -S unitdore ``` **Debian / Ubuntu** Download the `.deb` from the [releases page](https://git.warky.dev/wdevs/unitdore/releases) and install: ```bash sudo dpkg -i unitdore_*.deb ``` **CentOS / AlmaLinux / Rocky Linux** Download the `.rpm` from the [releases page](https://git.warky.dev/wdevs/unitdore/releases) and install: ```bash sudo rpm -i unitdore-*.rpm # or with dnf: sudo dnf install unitdore-*.rpm ``` **Gentoo (OpenRC / systemd)** Ebuild in `pkg/gentoo/app-containers/unitdore/`. Copy it into a local overlay, then: ```bash ebuild unitdore-.ebuild manifest # once per version sudo emerge app-containers/unitdore ``` **Void Linux (runit)** Template in `pkg/void/srcpkgs/unitdore/`. Copy it into a `void-packages` checkout, then: ```bash ./xbps-src pkg unitdore sudo xbps-install --repository=hostdir/binpkgs unitdore ``` Set `init: runit` / `init: openrc` in the config if autodetect picks the wrong one. **From source** ```bash make build sudo make install ``` Or manually: ```bash go build -o unitdore . sudo cp unitdore /usr/local/bin/ ``` ## Usage ### Discover running containers ```bash sudo unitdore syncup ``` Queries Podman and Docker for running containers, adds new ones to the config, and reconciles any that have disappeared (marking them disabled). - Podman first; Docker only for names Podman doesn't have - podman-compose containers → one `kind: stack` unit per project; existing member units disabled (`part of stack `) - Root: root's containers + rootless Podman containers of each user in `users:` - Normal user: own Podman containers, config `~/.config/unitdore/units.yaml`, user units only ### Edit the config ```bash sudo unitdore edit ``` Opens `/etc/unitdore/units.yaml` in `$EDITOR` (falls back to `nvim`, `nano`, `vi`; creates the config dir if missing). ### Install service files ```bash sudo unitdore install ``` Generates the service file for one enabled unit (error if unknown or disabled). Does not enable or start it. ```bash sudo unitdore install --dry-run # preview, write nothing sudo unitdore install --all # all enabled units; installed + disabled ones are stopped and removed sudo unitdore installall # same as install --all ``` ### Start / stop / enable / uninstall ```bash sudo unitdore start # start only sudo unitdore enable # enable at boot + start sudo unitdore stop sudo unitdore uninstall # stop, disable, remove service file ``` Each takes `--all` instead of a name to act on every unit (shortcuts: `startall`, `enableall`, `stopall`, `uninstallall`, `installall`). - `start/enable --all`: installed, enabled units in `order` (lowest first); same `order` → parallel - `stop/uninstall --all`: all installed units, reverse `order`, one at a time ### Order and delay at boot `order`/`delay` are written into the generated service files, so they also apply at boot. | Backend | `order` | `delay` | |---|---|---| | systemd | `After=` units of the previous group | `ExecStartPre=sleep N`, `TimeoutStartSec=N+90` | | OpenRC | `depend() { after … }` | `start_pre() { sleep N }` | | runit | `sv -w 30 check ` in `run` | `sleep N` in `run` | - Previous group = nearest lower `order` among enabled units of the same user - `delay` is the unit's own, ignored in the first group - Changing `order`/`enabled` via `update` regenerates all installed, enabled units - Delay also applies to manual `start` and to restarts - `delay` must be a valid non-negative duration (`5s`, `1m`); an invalid value fails config load ### Check status ```bash sudo unitdore status ``` Prints a summary table: ``` NAME RUNTIME USER ENABLED INSTALLED STATE REASON nginx podman root yes yes active myapp docker hein yes yes active oldthing podman hein no no — container not found ``` ## Config file **Location:** `/etc/unitdore/units.yaml` ```yaml init: "" # optional: systemd | openrc | runit (empty = autodetect) service_dir: "" # optional: runit supervised dir (empty = /var/service or /etc/service) prefix: "" # optional: prepended to all service names, e.g. "prod-" suffix: "" # optional: appended to all service names, e.g. "-svc" users: [hein] # root only: rootless users scanned by syncup units: - name: nginx runtime: podman # podman | docker user: "" # empty = root/system unit command: "" # optional: override ExecStart entirely order: 1 # startup order (lower = earlier) delay: 0s # wait before start (own delay, not first group) enabled: true disabled_reason: "" # auto-set by syncup reconciliation - name: myapp runtime: docker user: hein # rootless: generates user unit for this user order: 2 delay: 5s enabled: true - name: shop # = compose project name kind: stack # container (default) | stack runtime: podman user: hein project: shop working_dir: /srv/shop # from compose labels (refreshed by syncup) compose_files: [compose.yaml] compose_bin: /usr/bin/podman-compose order: 3 enabled: true - name: backup # shell script unit runtime: script script: /opt/scripts/backup.sh # required stop_script: /opt/scripts/stop.sh # optional → ExecStop shell: /bin/bash # optional, default /bin/bash service_type: simple # simple (default) | oneshot working_dir: /opt/scripts # optional order: 4 enabled: true - name: agent # executable unit runtime: exec executable: /opt/agent/bin/agent # required args: [--config, /etc/agent.toml] # optional stop_executable: /opt/agent/bin/stop # optional → ExecStop order: 5 enabled: true ``` ### Init systems Set `init:` in the config, or leave empty to autodetect (`/run/systemd/system` → systemd, `/run/openrc` → openrc, `/etc/runit` → runit, else systemd). | | systemd | openrc | runit | |---|---|---|---| | Service file | `/etc/systemd/system/unitdore-.service` | `/etc/init.d/unitdore-` | `/etc/sv/unitdore-/{run,finish}` + symlink in `service_dir` | | Start / stop | `systemctl` | `rc-service` | `sv` | | Enable at boot | `systemctl enable` | `rc-update add … default` | remove `down` file | | Installed state | disabled until enabled | not in runlevel | symlinked with `down` file (no autostart) | | Rootless `user:` | `systemctl --user` unit (needs linger) | system service, `command_user` | system service, `chpst -u` | | Non-root use | yes (own user units) | **root only** | **root only** | | `restart*` | `Restart=on-failure`, `StartLimitBurst` | `supervise-daemon` `respawn_*` (always respawns) | `finish`: on-failure, `RestartSec`; **no retry limit** | | `docker` dependency | `Requires=docker.service` | `need docker` | none | | `oneshot` | `RemainAfterExit` | `start()`/`stop()` functions | run once, then `tail -f /dev/null` | - Rootless units on openrc/runit set `HOME` and `XDG_RUNTIME_DIR=/run/user/` and create that dir if missing. - `command` / `args` use systemd quoting; they are re-quoted for sh on openrc/runit. - No log redirection is generated on openrc; use the runtime's own logs. - `docs/generated-units.md` shows systemd output only. ### Script / executable units Added manually (`unitdore edit`); syncup never creates or disables them. | Key | Runtime | Meaning | |---|---|---| | `script` | script | Required. `ExecStart=