* config: Add local_host and web_stack options
Allow configuring local_host and web_stack in compose-farm.yaml instead
of requiring environment variables. This makes it easier to deploy the
web UI with just a config file mount.
- local_host: specifies which host is "local" for Glances connectivity
- web_stack: identifies the web UI stack for self-update detection
Environment variables (CF_LOCAL_HOST, CF_WEB_STACK) still work as
fallback for backwards compatibility.
Closes#152
* docs: Clarify glances_stack is used by CLI and web UI
* config: Env vars override config, add docs
- Change precedence: environment variables now override config values
(follows 12-factor app pattern)
- Document all CF_* environment variables in configuration.md
- Update example-config.yaml to mention env var overrides
* config: Consolidate env vars, prefer config options
- Update docker-compose.yml to comment out CF_WEB_STACK and CF_LOCAL_HOST
(now prefer setting in compose-farm.yaml)
- Update init-env to comment out CF_LOCAL_HOST (can be set in config)
- Update docker-deployment.md with new "Config option" column
- Simplify troubleshooting to prefer config over env vars
* config: Generate CF_LOCAL_HOST with config alternative note
Instead of commenting out CF_LOCAL_HOST, generate it normally but add
a note in the comment that it can also be set as 'local_host' in config.
* config: Extend local_host to all web UI operations
When running the web UI in a Docker container, is_local() can't detect
which host the container is on due to different network namespaces.
Previously local_host/CF_LOCAL_HOST only affected Glances connectivity.
Now it also affects:
- Container exec/shell (runs locally instead of via SSH)
- File editing (uses local filesystem instead of SSH)
Added is_local_host() helper that checks CF_LOCAL_HOST/config.local_host
first, then falls back to is_local() detection.
* refactor: DRY get_web_stack helper, add tests
- Move get_web_stack to deps.py to avoid duplication in streaming.py
and actions.py
- Add tests for config.local_host and config.web_stack parsing
- Add tests for is_local_host, get_web_stack, and get_local_host helpers
- Tests verify env var precedence over config values
* glances: rely on CF_WEB_STACK for container mode
Restore docker-compose env defaults and document local_host scope.
* web: ignore local_host outside container
Document container-only behavior and adjust tests.
* web: infer local host from web_stack
Drop local_host config option and update docs/tests.
* Remove CF_LOCAL_HOST override
* refactor: move web_stack helpers to Config class
- Add get_web_stack() and get_local_host_from_web_stack() as Config methods
- Remove duplicate _get_local_host_from_web_stack() from glances.py and deps.py
- Update deps.py get_web_stack() to delegate to Config method
- Add comprehensive tests for the new Config methods
* config: remove web_stack config option
The web_stack config option was redundant since:
- In Docker, CF_WEB_STACK env var is always set
- Outside Docker, the container-specific behavior is disabled anyway
Simplify by only using the CF_WEB_STACK environment variable.
* refactor: remove get_web_stack wrapper from deps
Callers now use config.get_web_stack() directly instead of
going through a pointless wrapper function.
* prompts: add rule to identify pointless wrapper functions
Previously `cf config init-env` created the .env file next to the
compose-farm.yaml config file. This was unintuitive when working in
stack subdirectories - users expected the file in their current
directory.
Now the default is to create .env in the current working directory,
which matches typical CLI tool behavior. Use `-o /path/to/.env` to
specify a different location.
* docs: Clarify Docker Compose vs Compose Farm commands
Split the Usage section into two tables:
- Docker Compose Commands: wrappers with multi-host additions
- Compose Farm Commands: orchestration Docker Compose can't do
Also update the `update` command docstring to clarify it's
a shorthand for `up --pull --build`.
* chore(docs): update TOC
* docs: Add command type distinction to commands.md
Explain that commands are either Docker Compose wrappers with
multi-host superpowers, or Compose Farm originals for orchestration.
Also update `update` description to clarify it's a shorthand.
* Update README.md
* restart: Match Docker Compose semantics
Change `cf restart` from doing `down + up` to using `docker compose
restart`, matching the Docker Compose command behavior.
This provides command naming parity with Docker Compose. Users who want
the old behavior can use `cf down mystack && cf up mystack`.
- Update restart implementation to use `docker compose restart`
- Remove traefik regeneration from restart (no longer recreates containers)
- Update all documentation and help text
- Remove restart from self-update SSH handling (no longer involves down)
* web: Clarify Update tooltip uses 'recreate' not 'restart'
Avoid confusion now that 'restart' means something different.
* web: Fix Update All tooltip to use 'recreates'
* update: Only restart containers when images change
Use `up -d --pull always --build` instead of separate pull/build/down/up
steps. This avoids unnecessary container restarts when images haven't
changed.
* Update README.md
* docs: Update update command description across all docs
Reflect new behavior: only recreates containers if images changed.
* Update README.md
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* feat(docker): make container user configurable via CF_UID/CF_GID
Add support for running compose-farm containers as a non-root user
to preserve file ownership on mounted volumes. This prevents files
like compose-farm-state.yaml and web UI config edits from being
owned by root on NFS mounts.
Set CF_UID, CF_GID, and CF_HOME environment variables to run as
your user. Defaults to root (0:0) for backwards compatibility.
* docs: document non-root user configuration for Docker
- Add CF_UID/CF_GID/CF_HOME documentation to README and getting-started
- Add XDG config volume mount for backup/log persistence across restarts
- Update SSH volume examples to use CF_HOME variable
* fix(docker): allow non-root user access and add USER env for SSH
- Add `chmod 755 /root` to Dockerfile so non-root users can access
the installed tool at /root/.local/share/uv/tools/compose-farm
- Add USER environment variable to docker-compose.yml for SSH to work
when running as non-root (UID not in /etc/passwd)
- Update docs to include CF_USER in the setup instructions
- Support building from local source with SETUPTOOLS_SCM_PRETEND_VERSION
* fix(docker): revert local build changes, keep only chmod 755 /root
Remove the local source build logic that was added during testing.
The only required change is `chmod 755 /root` to allow non-root users
to access the installed tool.
* docs: add .envrc.example for direnv users
* docs: mention direnv option in README and getting-started
* refactor(web): store backups in XDG config directory
Move file backups from `.backups/` alongside the file to
`~/.config/compose-farm/backups/` (respecting XDG_CONFIG_HOME).
The original file path is mirrored inside to avoid name collisions.
* docs(web): document automatic backup location
* refactor(paths): extract shared config_dir() function
* fix(web): use path anchor for Windows compatibility
- Add Web UI page to navigation in zensical.toml
- Use absolute paths for video assets in web-ui.md
- Add web-workflow demo video to homepage Quick Demo section
Relative paths like `assets/install.webm` resolved incorrectly on
subpages (e.g., /getting-started/assets/install.webm instead of
/assets/install.webm), causing 404 errors for videos on those pages.
Explains how to set maproot_user/maproot_group to root/wheel
in TrueNAS to disable root squash, allowing Docker containers
running as root to write to NFS-mounted volumes.
Documents how to access child ZFS datasets over NFS by injecting
the crossmnt option into /etc/exports. Includes Python script and
setup instructions for cron-based persistence.
- Extract compose.py from traefik.py for generic compose parsing
(env loading, interpolation, ports, volumes, networks)
- Rename ssh.py to executor.py for clarity
- Extract operations.py from cli.py for business logic
(up_services, discover_running_services, preflight checks)
- Update CLAUDE.md with new architecture diagram
- Add docs/dev/future-improvements.md for low-priority items
CLI is now a thin layer that delegates to operations module.
All 70 tests pass.