Files
unitdore/README.md
T
warkanum f0827fcdd7 feat: podman-compose stack units and root/user execution scopes
- 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
2026-10-12 19:05:51 +02:00

223 lines
5.6 KiB
Markdown

# unitdore
> *A door you open and close for container units.*
Unitdore bridges your container runtime (Podman, Docker) and systemd. It discovers running containers and podman-compose stacks, stores them in a config file, and generates + manages systemd `.service` units 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
```
**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 <project>`)
- 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 `vi`).
### Install systemd unit files
```bash
sudo unitdore install
```
Generates `.service` files for all enabled units and writes them to systemd. Use `--dry-run` to preview without writing.
```bash
sudo unitdore install --dry-run
```
### Enable and start units
```bash
sudo unitdore active
```
Runs `systemctl enable --now` for all installed, enabled units in startup order.
### 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
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 # delay after previous order 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
```
### Stacks (podman-compose)
| Condition | Unit |
|---|---|
| `compose_bin`, `working_dir`, `compose_files` exist | `podman-compose -p <proj> -f <files> up` / `stop`, `Type=simple` |
| any missing | `podman pod start/stop pod_<proj>`, `Type=oneshot` |
| compose missing and no pod | `syncup` disables: `stack not found` |
### Users
| Run as | Config | Discovers | Units |
|---|---|---|---|
| root | `/etc/unitdore/units.yaml` | root + `users:` (Podman only for users) | system + user units |
| user | `~/.config/unitdore/units.yaml` | own Podman | own user units |
User units need linger to start at boot: `sudo loginctl enable-linger <user>` (`install` warns).
### Prefix and suffix
Set `prefix` and/or `suffix` to namespace your service files:
```yaml
prefix: prod-
suffix: ""
```
Generates: `unitdore-prod-nginx.service`, `unitdore-prod-myapp.service`, etc.
Prefix/suffix apply to the **service file name only** — container names are unchanged.
### Unnamed containers
If a container has no name, `syncup` uses its short container ID (first 12 chars) as the unit name.
You can rename it later with `unitdore edit`.
## Generated unit files
See **[docs/generated-units.md](docs/generated-units.md)** for full examples covering:
- System units (root)
- User units (rootless)
- Docker vs Podman
- Custom commands
- Compose stacks (compose + pod fallback)
- Prefix/suffix naming
- Startup ordering
- Unnamed containers (short ID fallback)
Quick example:
```ini
# /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
```
## Flags
```
--config string path to units config file (default "/etc/unitdore/units.yaml"; non-root: ~/.config/unitdore/units.yaml)
```
## Requirements
- Go 1.21+
- systemd
- Podman and/or Docker (only what you have installed is used)
- podman-compose for compose-mode stacks (pod fallback without it)
- Root for system units; the relevant user for rootless units