100 Commits
Author SHA1 Message Date
tiennm99 b3861f4c2f feat(gitea): move the database to PostgreSQL 18 2026-10-11 09:26:34 +07:00
tiennm99 8b33bd9135 feat(owncloud): upgrade to ownCloud 11 with MariaDB 10.11 and Redis 7 2026-10-11 09:20:28 +07:00
tiennm99 87604a2bd7 fix(couchbase): use the community edition image 2026-10-11 03:17:10 +07:00
tiennm99 51dea06fdd feat: add nextcloud, seafile, opencloud, collabora and onlyoffice services 2026-10-11 03:17:10 +07:00
tiennm99 9259273668 docs(skills): use the single miti-coolify MCP for both servers
miti-sg's Coolify now manages miti-jp too, so the skills search one MCP
server instead of two. Drop the stale gitea-mirror watch-path exception;
the new miti-jp app has its watch path set.
2026-10-11 02:33:02 +07:00
tiennm99 e6553e371a chore(skills): move gitea-mirror-maintenance to the workspace skills 2026-10-08 08:04:01 +07:00
tiennm99 c040eb25cf feat(gitea): disable open registration 2026-10-08 07:59:47 +07:00
tiennm99 3ea1b39e44 refactor: name supporting services and volumes by role
Rename database services to db and cache services to cache, with their
volumes as db-data and cache-data, in litellm, owncloud and goclaw. Rename
the couchbase and open-webui volumes to <service>-data. Data on miti-sg was
copied into the new volumes beforehand; the old volumes are kept.
2026-10-07 19:26:27 +07:00
tiennm99 29f52dfcb3 docs(hermes): record cron.wrap_response off so deliveries carry only the job output 2026-10-07 17:48:10 +07:00
tiennm99 672c686e5a docs(hermes): keep untagged group messages as context for mention-only replies 2026-10-07 17:09:33 +07:00
tiennm99 ac973a782f docs: record watch-path exceptions and Coolify env override precedence 2026-10-07 09:56:54 +07:00
tiennm99 fa4339562c docs(webtop): PASSWORD is the web login only, not a sudo password 2026-10-06 17:45:22 +07:00
tiennm99 e489b96793 docs: allow short main service names for verbose or generic images 2026-10-06 17:45:22 +07:00
tiennm99 59f8d94b97 docs: split agent rules into .claude/rules and list example role names
CLAUDE.md keeps the overview and deployment rules; naming, images and
comments, workspace services, and environment and secrets move to topic
files that load with it. Naming gains example role names for supporting
containers, as examples rather than a fixed list.
2026-10-06 17:31:31 +07:00
tiennm99 09506ae84f docs(webtop): abc can use docker without sudo
The universal-docker mod adds abc to the socket's group before the
desktop starts as abc.
2026-10-06 17:22:20 +07:00
tiennm99 bfd9fdbfd3 refactor(paseo): install gh, glab, uv and Python into home
Drop them from the image; the README gives the commands to install each
into ~/.local/bin on the home volume. System packages stay in the image.
2026-10-06 17:22:20 +07:00
tiennm99 7aafbd2d8b docs: name supporting services by role and track renames
Supporting containers are db, cache or a short role name; services
declare no networks since Coolify creates one per app. TODO lists the
services whose names still differ.
2026-10-06 17:22:20 +07:00
tiennm99 f38a3a2871 docs: revise install location and naming rules
Workspace services install tools into the home volume unless the package
is more than a binary. Prefer official images and treat upstream compose
files as a starting point. Name volumes <service>-<content> and networks
<main service>-network.
2026-10-06 17:09:38 +07:00
tiennm99 d889338306 fix(alloy): drop docker log lines older than Loki accepts
loki.source.docker resumes from a one-second position and Docker's since
is inclusive, so each restart re-reads a container's last logged second.
For idle containers those lines are older than 7 days and Grafana Cloud
Loki rejects the batch with 400 timestamp too old. Drop them before
loki.write; Loki would not have stored them anyway.
2026-10-06 16:56:46 +07:00
tiennm99 feb6f035c8 fix(alloy): mount /run/udev for diskstats device properties
node-exporter's diskstats collector reads udev device properties from
/run/udev/data; without the mount it logs an error and drops those labels.
2026-10-06 16:50:22 +07:00
tiennm99 fc85962f45 fix: restore HOST override in zsh workspace services
Coolify injects HOST=0.0.0.0 and zsh's prompt reads $HOST instead of the
real hostname, so hostname: alone shows user@0. Set HOST=${SERVICE_HOSTNAME}
again in code-server, code-server-lsio and paseo.
2026-10-06 16:45:13 +07:00
tiennm99 56111e9219 fix(alloy): pass the Grafana token as GCLOUD_RW_API_KEY
Fleet Management's self-monitoring pipelines read the token from that name;
without it their remote-write fails with 401 invalid token.
2026-10-06 16:45:13 +07:00
tiennm99 422eab8499 chore: drop HOST overrides, move known issue to docs, tidy comments and gitignores
- Remove HOST=${SERVICE_HOSTNAME} from code-server, code-server-lsio and paseo; hostname: alone sets the name
- Move SERVICE_HOSTNAME to the top of their .env.example to match compose order
- Drop the openclaw ARM64 Chromium note from its README; docs/openclaw covers it
- List CUSTOM_USER in webtop's setup step
- Trim reasoning from diun and paseo comments
- Delete stale gitea and gitea-mirror .gitignore files
- Add TODO.md for remaining naming and README issues
2026-10-06 16:32:32 +07:00
tiennm99 042d6bd6c0 chore: drop container labels from cadvisor, require webtop CUSTOM_USER, trim env template comments 2026-10-06 16:19:14 +07:00
tiennm99 4c5c6ed17e chore: name couchbase's service after its image 2026-10-06 16:05:42 +07:00
tiennm99 e4c74ef5cd chore: align alloy, diun and couchbase with repo conventions
alloy: move to latest tags (no moving major tag upstream), trim comments to what each line does.
diun: order .env.example like compose.yml.
couchbase: add .env.example, drop the version key, explain the published ports.
2026-10-06 16:03:37 +07:00
tiennm99 0637a02051 chore: drop container_name from couchbase 2026-10-06 15:57:40 +07:00
tiennm99 272db135b7 chore: drop container_name from alloy and traffmonetizer 2026-10-06 15:57:10 +07:00
tiennm99 98b0924ca7 chore: drop traffmonetizer version key, document its restart policy, add alloy .env.example 2026-10-06 15:55:36 +07:00
tiennm99 ee2f92de19 refactor(code-server): install Docker CLI, plugins and gh into home
- Drop the Docker and GitHub apt repositories and the Ruby build headers
  from the image; keep build-essential, bubblewrap, zip and unzip.
- Document home installs for the Docker CLI with its Compose and Buildx
  plugins, gh, Go, Rust, Python, Node and Java, ordered so each installer
  writes to ~/.zshrc and SDKMAN stays last.
- Drop the Ruby, GitLab CLI and jq instructions.
2026-10-06 14:43:56 +07:00
tiennm99 c1ef9e7680 feat(code-server): install Docker CLI and gh from vendor apt repos
- Install the Docker CLI with its Compose and Buildx plugins, and the GitHub
  CLI, from Docker's and GitHub's signed apt repositories; drop their home
  installs and the GitLab CLI from the README.
- Select the shell with SHELL and the start folder with DEFAULT_WORKSPACE
  instead of chsh and working_dir.
- Add an optional CODE_SERVER_APP_NAME.
- Document installing Java with SDKMAN.
2026-10-06 13:39:31 +07:00
tiennm99 03a2016f23 fix(code-server-lsio): require PASSWORD and correct the Docker group note 2026-10-06 13:39:26 +07:00
tiennm99 4c37d45377 feat(code-server): join the Docker socket's group at startup and add build tools
Add an entrypoint wrapper that reads the socket's GID, adds coder to a
matching group and re-execs the image's entrypoint under it, so docker
works without sudo on any host. Install build-essential and the headers
rbenv needs to build Ruby, and make zsh the login shell. Document home
installs for rbenv, Docker Compose and Buildx plugins, and jq.
2026-10-06 12:23:22 +07:00
tiennm99 56b1c5f931 fix(code-server): pass SERVICE_HOSTNAME as HOST so the zsh prompt shows the hostname 2026-10-06 11:11:39 +07:00
tiennm99 088579890a feat(code-server): add code-server from the official codercom image 2026-10-06 10:19:29 +07:00
tiennm99 18935e01a7 refactor: rename code-server-linuxserver to code-server-lsio and set naming rules for compose names 2026-10-06 08:58:11 +07:00
tiennm99 a7137fcdc4 refactor: rename the code-server service directory to code-server-linuxserver 2026-10-06 08:49:38 +07:00
tiennm99 85b8a5b1ef docs(hermes): document replying only when tagged in Telegram groups 2026-10-06 08:49:35 +07:00
tiennm99 614875f295 fix(gitea-mirror): run the health check every 5 minutes to stay under GitHub's anonymous rate limit 2026-10-05 18:44:51 +07:00
tiennm99 beaf805dcc refactor(gitea-mirror): name the Bun idle timeout variable HTTP_IDLE_TIMEOUT 2026-10-05 18:35:05 +07:00
tiennm99 82892042dc fix(hermes): configure chat platforms in /opt/data/.env, not Coolify
The gateway's allow-lists read only the profile .env under the default
multiplex mode, so Telegram values passed from the environment left every
user blocked. Drop the passthrough and document the dashboard as the one
place to set them, plus the dashboard secret's 16-byte minimum.
2026-10-05 17:35:34 +07:00
tiennm99 d4256617f4 feat(openclaw): set the startup env doctor recommends on ARM hosts
Add NODE_COMPILE_CACHE and OPENCLAW_NO_RESPAWN, and record the remaining
doctor notes and the ARM64 browser path override in docs/openclaw.
2026-10-05 16:33:27 +07:00
tiennm99 0a7e8f9783 feat(gitea-mirror): split gitea-mirror out of the gitea stack into its own service 2026-10-05 15:59:04 +07:00
tiennm99 5caa497d7f refactor: rename the gitea-mirror service directory to gitea 2026-10-05 15:54:03 +07:00
tiennm99 d0eb7c8aa9 docs: fix stale references after removing services 2026-10-05 14:54:37 +07:00
tiennm99 d5d7121180 chore: remove the opencode service 2026-10-05 14:52:03 +07:00
tiennm99 2831651bd2 fix(owncloud): give redis a named data volume, as upstream does 2026-10-05 14:52:03 +07:00
tiennm99 1cbbf271ce refactor: align openclaw and hermes with their official Docker guides 2026-10-05 14:52:03 +07:00
tiennm99 77c12d5397 feat(hermes): pass Telegram gateway settings through 2026-10-05 14:18:36 +07:00
tiennm99 0d0a48221b refactor: switch openclaw and hermes to their official images 2026-10-05 13:22:01 +07:00
tiennm99 8d4c3d14a4 feat: add openclaw and hermes agent templates 2026-10-05 11:22:18 +07:00
tiennm99 7616ff9f89 feat(owncloud): add ownCloud with MariaDB and Redis 2026-10-05 10:49:47 +07:00
tiennm99 0b660eb106 feat(webtop): add dev toolchain mods and Docker access 2026-10-05 10:26:55 +07:00
tiennm99 1bda841d1c chore(webtop): default CUSTOM_USER to miti99 2026-10-05 10:00:22 +07:00
tiennm99 9cc483f63b feat(webtop): add Ubuntu XFCE web desktop 2026-10-05 09:54:03 +07:00
tiennm99 8bd14da5ca feat(open-webui): add Open WebUI chat interface 2026-10-05 09:03:35 +07:00
tiennm99 40a234e2ff fix(litellm): build config.yaml into the image instead of bind-mounting it 2026-10-05 00:21:32 +07:00
tiennm99 8291a3408e feat(litellm): add LiteLLM proxy with PostgreSQL and Redis 2026-10-04 23:08:09 +07:00
tiennm99 f544979a91 feat: add migrate-service skill for moving Coolify deployments onto this repo 2026-10-04 22:24:09 +07:00
tiennm99 dd7c951143 chore: drop required-env header comments from compose files 2026-10-04 21:53:29 +07:00
tiennm99 01102bdf06 refactor: comment out optional env vars in diun and code-server 2026-10-04 21:53:29 +07:00
tiennm99 5844ef5611 refactor(goclaw): comment out optional MiniMax and trace env vars 2026-10-04 21:50:15 +07:00
tiennm99 1d2010bbe4 docs: list optional env vars commented out in compose files 2026-10-04 21:50:15 +07:00
tiennm99 acb3628dc8 feat(goclaw): pass through auto-upgrade and MiniMax env, add healthcheck 2026-10-04 21:42:01 +07:00
tiennm99 a0b5e15d9d chore: ignore local plans directory 2026-10-04 18:25:07 +07:00
tiennm99 198329c008 docs: move service issue notes from READMEs into docs/<service>
Service READMEs now cover only what the service is and how to deploy it.
Known issues, log noise and troubleshooting move to docs/<service>/, named
after the service directory, so editing them never redeploys the service.
Drop alloy's validate workflow, which never ran from a subdirectory.
2026-10-04 09:46:08 +07:00
tiennm99 b6952d84df refactor: move gitea-mirror maintenance skill to the repo root
A skill under gitea-mirror/ redeployed the stack on every edit, since the
Coolify watch path is gitea-mirror/**, and only loaded once a session touched
that directory. Service directories now hold deploy files only; skills live
in the root .claude/skills/.
2026-10-04 09:26:22 +07:00
tiennm99 e92ed7914d feat(gitea-mirror): clean up mirrors left by renamed and deleted repos
Add cleanup-renamed-repos.sh, which groups Gitea mirrors and gitea-mirror
rows by GitHub repo id, keeps the copy at the current name (renaming a
case-only mismatch in place), deletes the rest and re-imports.

cleanup-archived-repos.sh now deletes every mirror whose GitHub source 404s
when the source belongs to the gh user or an org it administers, keeps
third-party ones, and aborts on a GitHub rate limit.
2026-10-04 09:06:15 +07:00
tiennm99 bd0fe78994 fix(gitea-mirror): keep gitea-mirror waiting as long as Gitea clones
Gitea's migrate API stays silent until the clone ends, and Bun's fetch
drops a connection idle for 5 minutes. gitea-mirror then marked large
repositories failed while Gitea kept cloning, and a later retry took the
half-made repository for a finished mirror. GITEA_CLONE_TIMEOUT now also
sets BUN_CONFIG_HTTP_IDLE_TIMEOUT.
2026-10-03 13:38:02 +07:00
tiennm99 efd0848143 feat(gitea-mirror): add cleanup of archived copies of deleted repos
Deletes the archived-* Gitea copies gitea-mirror keeps when a source in the
given owners disappears, and removes their tracking rows so they are not
re-mirrored. Dry run by default.
2026-10-03 13:09:05 +07:00
tiennm99 4576b4dfd5 fix(gitea-mirror): match failed mirrors by source name in detection
gitea-mirror clears mirroredLocation when a mirror fails, so failed repos
were dropped from the status map and reported as status unknown.
2026-10-03 13:09:05 +07:00
tiennm99 9a9e4cddf7 refactor(gitea-mirror): run mirror maintenance through tea and the gitea-mirror API
The skill reached Gitea on localhost and the mirror database inside the
container, which only worked on a local host. It now uses tea for Gitea
and the gitea-mirror API key for repository status and retries, in bash.
Private upstreams are no longer probed anonymously, which reported them
as deleted.
2026-10-03 11:23:32 +07:00
tiennm99 a84e158738 feat(gitea-mirror): raise the Gitea clone timeout to one hour
Gitea's default migrate and fetch timeouts cut multi-gigabyte mirrors off
mid-clone. GITEA_CLONE_TIMEOUT sets both, defaulting to 3600 seconds.
2026-10-03 11:23:32 +07:00
tiennm99 d4882c6f3e fix(gitea-mirror): read auth and encryption secrets from the environment
Data encrypted under one secret is unreadable under another, so the
secrets must move with the data instead of being regenerated by the image.
2026-10-03 10:07:34 +07:00
tiennm99 3c737b05cd chore: add sources dir and debug-service skill, make Coolify primary
sources/ holds gitignored upstream checkouts for debugging a service
against its real code; the debug-service skill walks through it. Coolify
is now the primary deployment target and Dokploy optional.
2026-10-03 09:58:52 +07:00
tiennm99 67da7cd870 feat(gitea-mirror): serve gitea and gitea-mirror through the proxy
Drop the localhost-bound ports, read the database password and both
public URLs from the environment, pin gitea to its major tag, add a gitea
health check and disable gitea's SSH server.
2026-10-03 09:58:52 +07:00
tiennm99 b9042fb191 refactor: rename remaining docker-compose.yml files to compose.yml 2026-09-30 15:46:52 +07:00
tiennm99 ea5079fd4e feat(opencode): pass OPENCODE_API_KEY for OpenCode Go and Zen 2026-09-30 15:46:52 +07:00
tiennm99 4c66ec76e6 docs: keep each service README inside its own directory
A service README now describes only its own service: no links to other
services or to the root, and no restating of the shared conventions that
the root README and CLAUDE.md already carry. Each service is a separate
Coolify app on a <service>/** watch path, so a cross-link made editing one
service redeploy another. The rule is recorded at the root; the alloy
compose comment now points at a heading that exists.
2026-09-29 20:00:15 +07:00
tiennm99 daf18abf70 refactor(opencode): rename service directory from opencode-web to opencode 2026-09-29 19:36:11 +07:00
tiennm99 d211e42a4a chore(code-server): install bubblewrap 2026-09-28 15:54:27 +07:00
tiennm99 1d98f2ea95 chore(diun): check for updates once a day at midnight UTC 2026-09-21 08:41:48 +07:00
tiennm99 9da8546d19 feat(goclaw): add multi-tenant AI agent gateway with pgvector postgres 2026-09-20 14:34:57 +07:00
tiennm99 c2b508c171 chore: set restart: unless-stopped on every service
Coolify injects the same value when a compose service omits one, but Dokploy
runs the file as written, so in its default compose mode an omitted policy
leaves the container down after a crash or a host reboot.
2026-09-20 14:34:57 +07:00
tiennm99 b626c5b70d chore(diun): track moving image tags instead of exact pins
Diun follows the upstream major tag 4; the socket proxy follows latest,
since that project publishes no moving major tag.
2026-09-20 12:50:04 +07:00
tiennm99 ad122ab9f2 chore: drop the openhands service
Remove the directory and its row from the root table, the related-services link
in opencode-web, and the workspace-volume convention's reference to it.
2026-09-19 17:44:55 +07:00
tiennm99 24cefe80a2 feat(diun): add diun template reading the Docker API through a proxy
Diun needs the Docker API to enumerate containers and inspect each one's
image. Mounting the socket into it directly is host-root-equivalent, and :ro on
a socket mount is cosmetic, so the socket goes into a docker-socket-proxy
sidecar and Diun reaches it at tcp://dockerproxy:2375. POST is revoked there,
so container create and exec return 403.

CONTAINERS and IMAGES are both required: with CONTAINERS alone the provider
loads and enumerates containers, then every ImageInspect returns 403 and
nothing is analysed. Verified against a live watch cycle — 25 images analysed,
no errors.

Pin crazymax/diun:4.33 rather than :latest, since this is the service whose job
is to talk to the daemon.
2026-09-19 17:18:31 +07:00
tiennm99 c2502d5f44 chore: drop ollama, netdata, per-service licenses and stale plans
Rename gitea-mirror-local to gitea-mirror.
2026-09-19 12:43:05 +07:00
tiennm99 e6dcdfef48 docs(plans): add openhands/opencode-web plan and paseo docs audit 2026-09-18 17:43:03 +07:00
tiennm99 14a5720f7a chore: drop code-server-base
code-server covers the remaining use.
2026-09-18 17:38:07 +07:00
tiennm99 a63c121075 feat(code-server): give the workspace its own volume
The workspace moves off the config volume onto code-server-workspace, so
wiping editor state and wiping code are separate acts.

The image only ever chowns the literal path /config/workspace, and reads
DEFAULT_WORKSPACE to pick the folder to open, so a named volume on /workspace
would come up root-owned and unwritable. Creating the directory in a local
Dockerfile seeds the volume with the right ownership instead.
2026-09-18 17:38:07 +07:00
tiennm99 ff9ec68b0b fix(alloy): drop read_only so the inline compose config can be created
Compose materialises a configs: entry with inline content by writing it into
the container and refuses to do so on a read-only service: "cannot create
config ... : `file` is the sole supported option". The container was created
without /etc/alloy/config.alloy and the deployment failed at start.

Keeping the config inline matters more than the read-only rootfs, so the flag
and its tmpfs go. Every other control stays: no privileged, cap_drop ALL with
only DAC_OVERRIDE added, no-new-privileges, the socket proxy, and the limits.
2026-09-18 17:36:12 +07:00
tiennm99 0ef059dc31 refactor(alloy): drop privileged and proxy the docker socket read-only
Replace privileged: true with cap_drop ALL plus DAC_OVERRIDE, no-new-privileges,
a read-only rootfs and memory/pid limits. DAC_OVERRIDE is what lets the uid-0
entrypoint create its storage directory and read the journal and /rootfs; every
other capability stays dropped.

Route prometheus.exporter.cadvisor, discovery.docker and loki.source.docker
through a docker-socket-proxy sidecar on 127.0.0.1:2375 instead of bind-mounting
the socket. POST is refused there, so container create and exec are no longer
reachable. NETWORKS is granted because Docker SD resolves network names per
container and returns no targets without it.

Add an alloy validate step to CI and boot the test container with the shipped
capability set, read-only rootfs and proxy rather than --privileged.
2026-09-18 17:28:08 +07:00
tiennm99 99f4f8b820 feat: add openhands and opencode-web services
openhands runs each agent session in a container it spawns through the host
docker socket, so the socket is mounted read-write and host.docker.internal is
resolved. No host workspace is exposed.

opencode-web serves the opencode agent as a browser UI. The vendor image ships
only the opencode binary on bare Alpine, so a local Dockerfile adds bash, git,
curl and an ssh client.
2026-09-18 17:04:04 +07:00
tiennm99 3574c55e0a chore(code-server): mount the docker socket read-only
The flag does not restrict Docker API access; the README says so.
2026-09-18 16:40:01 +07:00
tiennm99 a82cfcc677 refactor(paseo): leave the home chown to the base entrypoint
The base image ships /home/paseo owned by uid 1000 and declares it a
volume, so a fresh named volume is seeded with that ownership, and the
base entrypoint chowns it and the agent config directories when they are
not.
2026-09-18 14:36:53 +07:00
tiennm99 c20aed29cb feat(paseo)!: drop go and sdkman, rename AGENT_CLIS to AGENTS
The image installs python, gh, glab, build-essential, sudo, zsh and nano
only; go or a jvm goes into $HOME from a terminal instead.

SHELL and TZ are written into compose.yml rather than read from .env, so
the deploying shell's own values can no longer win over them.

The entrypoint calls chpasswd, chown and gosu by absolute path, tolerates
a chpasswd failure instead of taking the start down with it, turns
globbing off around the agent loop, and counts an agent as installed only
when its binary actually runs.
2026-09-18 11:39:09 +07:00
tiennm99 ac00eb9674 style: order environment variables by how much the service needs them
environment: blocks were in no particular order. They now run must-have ->
should-have -> optional, with related variables kept adjacent as a group that
takes the tier of its most important member: PUID/PGID, PASSWORD with
SUDO_PASSWORD, DOCKER_MODS ahead of the INSTALL_PACKAGES and
NODEJS_MOD_VERSION that configure it, the four GIT_* entries, the PASEO_*
daemon settings.

Each .env.example is reordered to match its compose file. The names do not map
one to one -- PASSWORD feeds both PASSWORD and SUDO_PASSWORD, SERVICE_HOSTNAME
feeds HOST -- so an entry sits where the first compose entry reading it sits.

The HOST comment in both compose files is dropped; the READMEs already carry
that explanation in full. CLAUDE.md records the ordering convention.

alloy and gitea-mirror-local are untouched: every variable there is required,
so the tiers collapse and the existing grouping is the better one.
2026-09-18 10:47:56 +07:00
tiennm99 ea46992e64 chore(code-server): drop the pnpm mod and trim installed packages
pnpm is not used anywhere -- npm is the package manager everywhere -- so the
mod that installs it is dead weight on every container start.

INSTALL_PACKAGES loses apache2-utils, bfs, ffmpeg, imagemagick, librsvg2-bin,
lsof, psmisc and ugrep, and gains glab. Each entry is re-resolved by apt on
every start, so the list is kept to what is actually reached for.
2026-09-18 10:47:46 +07:00
tiennm99 6924ba0424 docs: keep personal values out of the examples
Record the convention in CLAUDE.md: .env.example is a template, so its values
stay generic and the real ones are set per deployment in Coolify or Dokploy.
Note the naming trap alongside it -- Compose interpolation reads the deploying
shell's environment before the .env file, so a variable must not collide with
one the shell already exports.

The alloy README's example host is made generic to match.
2026-09-18 09:38:35 +07:00