From d1824a79dac6cf75698cd98f20ddf9000b0889d1 Mon Sep 17 00:00:00 2001 From: Matti Airas Date: Tue, 20 Oct 2026 16:10:05 +0300 Subject: [PATCH 1/3] docs: document compose.d app overrides Container app packages from container-packaging-tools v0.13.0 accept local Compose overrides in compose.d/, applied through a compose wrapper. Describe the wrapper-based service unit in the package system page, add an admin guide to app-data, and add the failed-start recovery to troubleshooting. Co-Authored-By: Claude Opus 5.5 --- docs/architecture/package-system.md | 10 ++++--- docs/user-guide/app-data.md | 42 ++++++++++++++++++++++++++++- docs/user-guide/troubleshooting.md | 27 +++++++++++++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/docs/architecture/package-system.md b/docs/architecture/package-system.md index 4f7edd5..17d0e36 100644 --- a/docs/architecture/package-system.md +++ b/docs/architecture/package-system.md @@ -20,6 +20,8 @@ Each container app package contains: ``` /var/lib/container-apps/{app-name}/ ├── docker-compose.yml # Docker Compose definition +├── compose # Wrapper that runs docker compose with the overrides +├── compose.d/ # Local Compose overrides (empty when installed) ├── runtime.env # Environment variables └── icon.png # Application icon @@ -32,7 +34,9 @@ Each container app package contains: The package is intentionally lightweight — it does **not** contain the Docker image itself. The actual container image is pulled from the registry when the service starts for the first time. This keeps packages small (a few kilobytes) and ensures you always get the exact image version specified in the compose file. -The systemd service runs `docker compose up -d` on start and `docker compose down` on stop. +The systemd service runs Compose through the `compose` wrapper, which passes `docker-compose.yml`, then the generated routing-labels file for routed apps, then any files in `compose.d/`. Before starting, the service runs `prestart.sh` and `compose config --quiet`, so an invalid project fails the start. It then runs `compose up` in the foreground, and on stop runs `docker compose -p down --remove-orphans`. The stop uses only the project name, so it does not depend on the files in `compose.d/`. + +`compose.d/` holds local overrides written by the administrator. See [App Data and Migration — Customising an app with compose.d](../user-guide/app-data.md#customising-an-app-with-composed). ## App Definition Format @@ -177,10 +181,10 @@ apt remove marine-grafana-container ``` apt upgrade - → dpkg extracts updated files + → dpkg extracts updated files, overwriting docker-compose.yml → systemd restarts the service → docker compose pulls new image (only if upstream version changed) and recreates container - → Persistent data in /var/lib/container-apps/{app}/ is preserved + → compose.d/ overrides and persistent data in /var/lib/container-apps/{app}/ are preserved ``` ## Version Management diff --git a/docs/user-guide/app-data.md b/docs/user-guide/app-data.md index 43fef6d..d071e8c 100644 --- a/docs/user-guide/app-data.md +++ b/docs/user-guide/app-data.md @@ -1,6 +1,6 @@ # App Data and Migration -Every container app in HaLOS stores its persistent data on the host filesystem in a predictable location. This page explains where that data lives and how to migrate or back it up. +Every container app in HaLOS stores its persistent data on the host filesystem in a predictable location. This page explains where that data lives, how to migrate or back it up, and how to customise an app's Compose settings. ## Where app data is stored @@ -103,3 +103,43 @@ sudo tar xzf backup.tar.gz -C / # Start the app sudo systemctl start ``` + +## Customising an app with compose.d + +Each container app is defined by a `docker-compose.yml` in `/var/lib/container-apps//`. Do not edit that file: the next package upgrade overwrites it. To change an app's Compose settings, put your own Compose file in the app's `compose.d/` directory: + +``` +/var/lib/container-apps//compose.d/ +``` + +Files ending in `.yml` or `.yaml` are applied in byte order of filename, after `docker-compose.yml` and, for apps with a web address, after the generated routing labels. A file in `compose.d/` can therefore override any setting of the app. Package upgrades and `apt purge` leave these files in place. + +The files merge with the app's definition by Docker Compose's [merge rules](https://docs.docker.com/reference/compose-file/merge/). Lists such as `group_add` add to the existing entries. To clear a setting, tag it with `!reset`; to replace it entirely, tag it with `!override`. HaLOS ships Compose 2.26, which supports both. + +### Example: adding a supplementary group + +To give the process in one of the app's services an extra group, create for example `compose.d/10-groups.yml`: + +```yaml +services: + : + group_add: + - "" +``` + +`` is the service name from the app's `docker-compose.yml`. Use the numeric group ID from the host (`getent group ` shows it); a group name is looked up inside the container, where it may not exist. + +Relative paths in a `compose.d/` file resolve against the app directory, `/var/lib/container-apps//`. A top-level `name:` in a `compose.d/` file has no effect, because the service sets the project name itself. + +### Applying the change + +Changes take effect when the app restarts. Check the result first, then restart: + +```bash +sudo /var/lib/container-apps//compose check +sudo systemctl restart +``` + +`compose check` validates the app's Compose configuration with the same environment the service uses, and lists the active `compose.d/` files. It needs `sudo` because the app's `runtime.env` is readable only by root. + +If a `compose.d/` file makes the configuration invalid, the app does not start. See [App fails to start after adding a compose.d file](troubleshooting.md#app-fails-to-start-after-adding-a-composed-file). diff --git a/docs/user-guide/troubleshooting.md b/docs/user-guide/troubleshooting.md index 7bafe07..c3f402c 100644 --- a/docs/user-guide/troubleshooting.md +++ b/docs/user-guide/troubleshooting.md @@ -199,6 +199,33 @@ sudo systemctl restart avahi-daemon sudo systemctl status docker ``` +## App fails to start after adding a compose.d file + +**Symptom**: After you add or change a file in an app's `compose.d/` directory, the app's service fails to start. + +**Cause**: The service validates the app's Compose configuration, including every `compose.d/` file, before it starts the containers. An invalid file fails the start. + +**Solution**: + +1. Find the error in the service log: + ```bash + sudo journalctl -u -e + ``` + A `compose.d fragments: ...` line lists the active files, followed by Compose's error. The error names the service or the YAML line but not the file, so check each listed file. + +2. Fix or remove the file, then check the configuration: + ```bash + sudo /var/lib/container-apps//compose check + ``` + +3. Clear the failed state and start the service: + ```bash + sudo systemctl reset-failed + sudo systemctl start + ``` + +See [Customising an app with compose.d](app-data.md#customising-an-app-with-composed) for how these files are applied. + ## Getting help - **[GitHub Discussions](https://github.com/halos-org/halos-distro/discussions)** — Ask questions and share ideas with the community From c21c8abcce249e476d1c3b93518759057292c082 Mon Sep 17 00:00:00 2001 From: Matti Airas Date: Tue, 20 Oct 2026 16:10:57 +0300 Subject: [PATCH 2/3] docs: correct package layout, unit names and repo The file tree, unit names, APT repository and env file locations in package-system.md did not match what container-packaging-tools generates. Co-Authored-By: Claude Opus 5.5 --- docs/architecture/package-system.md | 33 ++++++++++++++++++----------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/docs/architecture/package-system.md b/docs/architecture/package-system.md index 17d0e36..69bc334 100644 --- a/docs/architecture/package-system.md +++ b/docs/architecture/package-system.md @@ -11,25 +11,34 @@ Packaging containers as `.deb` files provides several advantages over Docker Com - **Clean removal**: `apt remove` stops containers and cleans up - **Automatic updates**: Standard `apt upgrade` workflow - **System integration**: systemd services, icons, dashboard tiles — all in one package -- **Repository distribution**: Published to `apt.hatlabs.fi` like any APT package +- **Repository distribution**: Published to `apt.halos.fi` like any APT package ## What's in a Container App Package? Each container app package contains: ``` -/var/lib/container-apps/{app-name}/ +/var/lib/container-apps/{package}/ ├── docker-compose.yml # Docker Compose definition ├── compose # Wrapper that runs docker compose with the overrides ├── compose.d/ # Local Compose overrides (empty when installed) -├── runtime.env # Environment variables -└── icon.png # Application icon +├── prestart.sh # Runs before each start; writes the runtime environment +├── metadata.yaml # App metadata +├── config.yml # User-configurable settings +└── env.template # Default environment values + +/etc/container-apps/{package}/ +├── env.defaults # Default environment, rewritten on each upgrade +└── env # Local environment overrides, kept across upgrades + +/run/container-apps/{package}/ +└── runtime.env # Environment generated by prestart.sh at each start /etc/systemd/system/ -└── {app-name}-container.service # systemd service unit +└── {package}.service # systemd service unit /usr/share/pixmaps/ -└── {app-name}.png # Icon for store and dashboard +└── {package}.svg # Icon for store and dashboard ``` The package is intentionally lightweight — it does **not** contain the Docker image itself. The actual container image is pulled from the registry when the service starts for the first time. This keeps packages small (a few kilobytes) and ensures you always get the exact image version specified in the compose file. @@ -81,7 +90,7 @@ Standard Docker Compose file. The `container-packaging-tools` adds Traefik label ### config.yml (optional) -User-configurable settings that are exposed as environment variables in `runtime.env`. +User-configurable settings that are exposed as environment variables through `/etc/container-apps/{package}/env.defaults`, and overridable in `/etc/container-apps/{package}/env`. ## container-packaging-tools @@ -89,7 +98,7 @@ The `generate-container-packages` command converts app definitions into Debian p ``` apps/grafana/ generate-container-packages -├── metadata.yaml ──────────────────────────► grafana-container_12.3.2-1_all.deb +├── metadata.yaml ──────────────────────────► marine-grafana-container_12.3.2-1_all.deb ├── docker-compose.yml ├── config.yml └── icon.png @@ -140,7 +149,7 @@ New categories appear automatically when a package uses a new `category::*` tag ## APT Repository -Container app packages are published to `apt.hatlabs.fi`: +Container app packages are published to `apt.halos.fi`: | Channel | Purpose | Tag format | |---------|---------|-----------| @@ -157,7 +166,7 @@ The CI/CD pipeline builds packages on push to main, publishes to unstable, and p apt install marine-grafana-container → dpkg extracts files (seconds — package is small) → postinst creates data directories, generates secrets - → systemd starts grafana-container.service + → systemd starts marine-grafana-container.service → prestart.sh computes runtime configuration → docker compose up → Docker pulls image layers from registry (may take minutes on first install) @@ -170,7 +179,7 @@ apt install marine-grafana-container ``` apt remove marine-grafana-container - → prerm stops grafana-container.service + → prerm stops marine-grafana-container.service → docker compose down → Traefik removes route → Homarr adapter detects removal @@ -184,7 +193,7 @@ apt upgrade → dpkg extracts updated files, overwriting docker-compose.yml → systemd restarts the service → docker compose pulls new image (only if upstream version changed) and recreates container - → compose.d/ overrides and persistent data in /var/lib/container-apps/{app}/ are preserved + → compose.d/ overrides and persistent data in /var/lib/container-apps/{package}/data/ are preserved ``` ## Version Management From a2182a180c8d597efb363c49eedf078439935cc5 Mon Sep 17 00:00:00 2001 From: Matti Airas Date: Tue, 20 Oct 2026 16:14:09 +0300 Subject: [PATCH 3/3] docs: fix unit names, repo, purge and tree claims Review of the compose.d docs found the same wrong claims on other pages, and a few more in package-system.md: unit names, APT repository, what purge removes, config.yml being optional, icon extension, and which files the package itself installs. Co-Authored-By: Claude Opus 5.5 --- docs/architecture/package-system.md | 14 ++++++++------ docs/developer-guide/adding-apps.md | 2 +- docs/developer-guide/development-workflow.md | 4 ++-- docs/user-guide/app-data.md | 4 ++-- docs/user-guide/installing-apps.md | 4 ++-- docs/user-guide/system-management.md | 2 +- 6 files changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/architecture/package-system.md b/docs/architecture/package-system.md index 69bc334..e9aa881 100644 --- a/docs/architecture/package-system.md +++ b/docs/architecture/package-system.md @@ -15,7 +15,7 @@ Packaging containers as `.deb` files provides several advantages over Docker Com ## What's in a Container App Package? -Each container app package contains: +A container app package installs the files under `/var/lib`, `/etc/systemd` and `/usr/share`. Its maintainer scripts and service create the rest: ``` /var/lib/container-apps/{package}/ @@ -24,12 +24,14 @@ Each container app package contains: ├── compose.d/ # Local Compose overrides (empty when installed) ├── prestart.sh # Runs before each start; writes the runtime environment ├── metadata.yaml # App metadata +├── env.user-template # Template for the local env file ├── config.yml # User-configurable settings -└── env.template # Default environment values +├── env.template # Default environment values +└── data/ # Persistent app data, kept on remove and purge /etc/container-apps/{package}/ -├── env.defaults # Default environment, rewritten on each upgrade -└── env # Local environment overrides, kept across upgrades +├── env.defaults # Created by postinst from env.template on each install and upgrade +└── env # Created by postinst once; local overrides, kept across upgrades /run/container-apps/{package}/ └── runtime.env # Environment generated by prestart.sh at each start @@ -38,7 +40,7 @@ Each container app package contains: └── {package}.service # systemd service unit /usr/share/pixmaps/ -└── {package}.svg # Icon for store and dashboard +└── {package}.{svg,png} # Icon for store and dashboard (extension of the source icon) ``` The package is intentionally lightweight — it does **not** contain the Docker image itself. The actual container image is pulled from the registry when the service starts for the first time. This keeps packages small (a few kilobytes) and ensures you always get the exact image version specified in the compose file. @@ -88,7 +90,7 @@ See the [Container Metadata Reference](../reference/container-metadata.md) for t Standard Docker Compose file. The `container-packaging-tools` adds Traefik labels and Homarr labels automatically based on `metadata.yaml`. -### config.yml (optional) +### config.yml User-configurable settings that are exposed as environment variables through `/etc/container-apps/{package}/env.defaults`, and overridable in `/etc/container-apps/{package}/env`. diff --git a/docs/developer-guide/adding-apps.md b/docs/developer-guide/adding-apps.md index 4c29da9..4c83c61 100644 --- a/docs/developer-guide/adding-apps.md +++ b/docs/developer-guide/adding-apps.md @@ -163,7 +163,7 @@ settings: description: "Application timezone" ``` -These become environment variables in `runtime.env`, editable through the Cockpit configuration UI. +Their defaults land in `/etc/container-apps//env.defaults` and local values in `/etc/container-apps//env`, editable through the Cockpit configuration UI. The service reads both as environment variables. ## Step 4: Add an Icon diff --git a/docs/developer-guide/development-workflow.md b/docs/developer-guide/development-workflow.md index 69fb7a2..72ca64a 100644 --- a/docs/developer-guide/development-workflow.md +++ b/docs/developer-guide/development-workflow.md @@ -149,7 +149,7 @@ git commit --no-verify -m "WIP: message" 1. CI builds the Debian package 2. Creates a pre-release tag (`v{version}+{N}_pre`) -3. Publishes `.deb` to `apt.hatlabs.fi` unstable channel +3. Publishes `.deb` to the unstable channel of `apt.halos.fi` (`apt.hatlabs.fi` for repos in the `hatlabs` organisation) 4. Creates a draft GitHub release ### Publishing Stable Releases @@ -157,7 +157,7 @@ git commit --no-verify -m "WIP: message" 1. Test the unstable package on a device 2. Publish the draft release in GitHub UI 3. CI copies the `.deb` to the stable release -4. CI publishes to `apt.hatlabs.fi` stable channel +4. CI publishes to the stable channel of the same repository ## Development Environments diff --git a/docs/user-guide/app-data.md b/docs/user-guide/app-data.md index d071e8c..165ef5f 100644 --- a/docs/user-guide/app-data.md +++ b/docs/user-guide/app-data.md @@ -10,7 +10,7 @@ All container apps store data under: /var/lib/container-apps//data/ ``` -Each app gets its own isolated directory. Data persists across container restarts and app updates. Removing an app with `apt remove` preserves the data; `apt purge` deletes it. +Each app gets its own isolated directory. Data persists across container restarts and app updates. Removing an app with `apt remove` or `apt purge` preserves the data. `apt purge` deletes only the app's configuration in `/etc/container-apps//`; for a clean reinstall, delete `/var/lib/container-apps//` by hand after purging. ### Finding an app's data path @@ -140,6 +140,6 @@ sudo /var/lib/container-apps//compose check sudo systemctl restart ``` -`compose check` validates the app's Compose configuration with the same environment the service uses, and lists the active `compose.d/` files. It needs `sudo` because the app's `runtime.env` is readable only by root. +`compose check` validates the app's Compose configuration with the same environment the service uses, and lists the active `compose.d/` files. Part of that environment is generated when the service starts, so run the check while the app is running or after it has started at least once since boot. It needs `sudo` because the app's `runtime.env` is readable only by root. If a `compose.d/` file makes the configuration invalid, the app does not start. See [App fails to start after adding a compose.d file](troubleshooting.md#app-fails-to-start-after-adding-a-composed-file). diff --git a/docs/user-guide/installing-apps.md b/docs/user-guide/installing-apps.md index 3d4b98d..9239c4d 100644 --- a/docs/user-guide/installing-apps.md +++ b/docs/user-guide/installing-apps.md @@ -38,12 +38,12 @@ The store shows apps from all configured stores. The Marine variant adds a dedic ## Managing installed apps -**Start/Stop/Restart**: Open Cockpit → Services. Find the service (named `-container.service`) and use the controls to start, stop, or restart it. +**Start/Stop/Restart**: Open Cockpit → Services. Find the service (named `.service`) and use the controls to start, stop, or restart it. **View logs**: Open Cockpit → Services → select the service → Logs tab. Or use the terminal: ```bash -sudo journalctl -u -container.service -f +sudo journalctl -u .service -f ``` **Check container status**: Via the Cockpit terminal: diff --git a/docs/user-guide/system-management.md b/docs/user-guide/system-management.md index b16606b..baf3f85 100644 --- a/docs/user-guide/system-management.md +++ b/docs/user-guide/system-management.md @@ -33,7 +33,7 @@ A full command-line terminal in the browser. Useful for running commands without ### Services -View and manage systemd services. You can start, stop, restart, and enable/disable services. Container apps run as systemd services (e.g., `signalk-server-container.service`), so you can manage them here. +View and manage systemd services. You can start, stop, restart, and enable/disable services. Container apps run as systemd services (e.g., `marine-signalk-server-container.service`), so you can manage them here. ### Logs