diff --git a/README.md b/README.md index 8e45cb8..1817bba 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@ # composes My docker compose collection — one directory per service, each self-contained. +Tuned to my own setup rather than written as general-purpose templates. -These are tuned to my own setup, not written as general-purpose templates. They -are deployed through [Coolify](https://coolify.io) and -[Dokploy](https://dokploy.com), so they lean on the platform for things a -standalone compose file would normally declare: +Services are deployed through [Coolify](https://coolify.io) and +[Dokploy](https://dokploy.com), which own what a standalone compose file would +otherwise declare: -- **No published ports.** Both platforms attach the container to their proxy - network and map a domain directly to the internal port, so `ports:` is - unnecessary — and adding it would expose the host port as well. +- **No published ports.** The platform attaches the container to its proxy + network and maps a domain to the internal port. Publishing one would also + expose it on the host. - **No `restart:` policy.** The platform manages the container lifecycle. +- **No `container_name:`.** Compose derives it from the directory. -Treat them as working examples rather than drop-in configs. Running one with -plain `docker compose` means adding whatever your setup needs. +Services that do publish ports or set `restart:` say so in their own README. ## Layout @@ -33,7 +33,7 @@ the `code-server` project with its own network and volumes. In Coolify or Dokploy, point a Docker Compose resource at the service directory and set the environment variables from its `.env.example`. -Locally, for a quick check: +Locally: ```sh cd @@ -43,8 +43,8 @@ docker compose logs -f docker compose down ``` -`.env` is picked up automatically because it sits next to `compose.yml`. -Never commit it — the root `.gitignore` covers `.env`/`*.env` and re-includes +`.env` is picked up automatically because it sits next to `compose.yml`. Never +commit it — the root `.gitignore` covers `.env`/`*.env` and re-includes `.env.example`. ## Services @@ -53,17 +53,15 @@ Each links to its own README for variables, ports, and storage. | Service | What it is | | --- | --- | -| [alloy](alloy/README.md) | Grafana Alloy shipping host + Docker metrics/logs to Grafana Cloud | +| [alloy](alloy/README.md) | Grafana Alloy shipping host and Docker telemetry to Grafana Cloud | | [code-server](code-server/README.md) | VS Code in the browser, as a remote dev box | | [couchbase](couchbase/README.md) | Couchbase Server | -| [gitea-mirror-local](gitea-mirror-local/README.md) | Self-hosted Gitea + PostgreSQL + gitea-mirror for mirroring GitHub repos | +| [gitea-mirror-local](gitea-mirror-local/README.md) | Gitea + PostgreSQL + gitea-mirror, mirroring GitHub repos | | [netdata](netdata/README.md) | Netdata monitoring agent | -| [ollama](ollama/README.md) | Ollama, optionally with a web UI | +| [ollama](ollama/README.md) | Ollama LLM server | | [openvpn-as](openvpn-as/README.md) | OpenVPN Access Server | | [paseo](paseo/README.md) | Paseo coding-agent daemon and web UI | | [tastyigniter](tastyigniter/README.md) | TastyIgniter restaurant ordering platform | | [traffmonetizer](traffmonetizer/README.md) | TraffMonetizer bandwidth-sharing client | -The absorbed services (everything but `code-server`) predate this collection's -conventions — some still publish ports or set `restart:`; align them as they -get touched. +Licensed under Apache 2.0 — see [LICENSE](LICENSE). diff --git a/alloy/README.md b/alloy/README.md index 63a8fc0..34e9a3d 100644 --- a/alloy/README.md +++ b/alloy/README.md @@ -1,77 +1,90 @@ -# alloy-docker-compose +# alloy -One-file [Grafana Alloy](https://grafana.com/docs/alloy/latest/) setup that ships host (linux) and container (docker) telemetry to Grafana Cloud, plus pulls remote config from Grafana Fleet Management. +[Grafana Alloy](https://grafana.com/docs/alloy/latest/) shipping host and +container telemetry to Grafana Cloud, with remote config from Grafana Fleet +Management. -- **Single container** running both `node_exporter` (host) and `cadvisor` (containers) collectors. -- **Config embedded inline** via Compose `configs:` — no sidecar `config.alloy` file on disk. -- **Env-driven** — nine shell variables, no `.env` file. -- **Remote config** via `remotecfg` block (Grafana Fleet Management). +One container runs both the `node_exporter` (host) and `cadvisor` (container) +collectors. The Alloy config is embedded inline via Compose `configs:`, so +there is no `config.alloy` on disk, and every setting comes from a shell +variable rather than a `.env` file. + +Uses `docker-compose.yml`, and sets `restart: unless-stopped` and +`container_name: alloy` — unlike the platform-managed services described in the +[root README](../README.md). ## What it collects | Source | Component | Notes | |---|---|---| -| Host metrics | `prometheus.exporter.unix` | CPU, memory, load, disk I/O, filesystem, network, uname, boot time, systemd, vmstat, sockstat — full default-collector set minus `ipvs/btrfs/infiniband/xfs/zfs` | +| Host metrics | `prometheus.exporter.unix` | CPU, memory, load, disk I/O, filesystem, network, uname, boot time, systemd, vmstat, sockstat — the default collector set minus `ipvs/btrfs/infiniband/xfs/zfs` | | Container metrics | `prometheus.exporter.cadvisor` | CPU, memory, fs usage/limit, network, `last_seen` | -| Container logs | `loki.source.docker` | all running containers, labeled with `container`, `stream`, `instance` | -| System logs (journal) | `loki.source.journal` (via `journal_module`) | systemd journal with `unit`, `boot_id`, `transport`, `level` labels | -| System logs (files) | `loki.source.file` | `/var/log/syslog`, `/var/log/messages`, `/var/log/*.log` | -| Remote config | `remotecfg` | polls Grafana Fleet Management every 60s | +| Container logs | `loki.source.docker` | All running containers, labeled `container`, `stream`, `instance` | +| Journal logs | `loki.source.journal` | systemd journal, labeled `unit`, `boot_id`, `transport`, `level` | +| File logs | `loki.source.file` | `/var/log/syslog`, `/var/log/messages`, `/var/log/*.log` | +| Remote config | `remotecfg` | Polls Grafana Fleet Management every 60s | -Filtering follows the upstream Grafana Cloud integration configs verbatim — `keep`-lists copied from each integration's **Metrics** section ([Linux Node](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-linux-node/#metrics), [Docker](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-docker/#metrics)). Logs are unfiltered. +Metric filtering copies the `keep`-lists from the upstream Grafana Cloud +integrations verbatim ([Linux Node](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-linux-node/#metrics), +[Docker](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-docker/#metrics)). +Logs are unfiltered. -> **Log duplication caveat.** On systems where rsyslog mirrors journald to `/var/log/syslog` (e.g. Debian/Ubuntu defaults), enabling both pipelines double-ships the same lines. If that's the case for your hosts, drop one source — typically the file-based one is redundant on systemd-only stacks. +Where rsyslog mirrors journald into `/var/log/syslog` — the Debian and Ubuntu +default — the journal and file pipelines double-ship the same lines. Drop one +source on those hosts; the file-based one is the redundant one on systemd-only +stacks. -## Quick start +## Environment + +All nine are required; `docker compose up` fails fast if any is unset. + +| Variable | Purpose | +| --- | --- | +| `ALLOY_HOSTNAME` | Container hostname, and the Loki/Prometheus `instance` label | +| `REMOTECFG_URL` | Fleet Management endpoint | +| `REMOTECFG_ID` | Fleet Management agent id | +| `REMOTECFG_USER` | Fleet Management user id | +| `PROM_URL` / `PROM_USER` | Prometheus remote-write endpoint and user id | +| `LOKI_URL` / `LOKI_USER` | Loki push endpoint and user id | +| `GRAFANA_TOKEN` | One Cloud Access Policy token, scopes `metrics:write` + `logs:write` + `fleet-management:read` | + +Find the values under Grafana Cloud → your stack → **Details** on each data +source, and under Fleet Management. The same token serves `remotecfg`, +Prometheus and Loki basic-auth. ```bash -export ALLOY_HOSTNAME=miti-jp # also used as Loki/Prometheus instance label -export REMOTECFG_URL=https://fleet-management-prod-013.grafana.net -export REMOTECFG_ID=miti-jp # fleet-management agent id -export REMOTECFG_USER=1431677 # fleet-management user id -export PROM_URL=https://prometheus-prod-XX-.grafana.net/api/prom/push -export PROM_USER= -export LOKI_URL=https://logs-prod-XXX.grafana.net/loki/api/v1/push -export LOKI_USER= -export GRAFANA_TOKEN=glc_... # one Cloud Access Policy token, scopes: metrics:write + logs:write + fleet-management:read - +export ALLOY_HOSTNAME=miti-jp REMOTECFG_ID=miti-jp ... docker compose up -d ``` -Find the `PROM_*` / `LOKI_*` / `REMOTECFG_*` values under Grafana Cloud → your stack → **Details** on each data source / Fleet Management. The same token is reused for `remotecfg`, Prometheus, and Loki basic-auth. +Run the same file on every host, changing `ALLOY_HOSTNAME` and `REMOTECFG_ID` +per host. Filter in Grafana with `instance=~"..."`. -Any unset required variable makes `docker compose up` fail fast. +## Privileges -## Multi-host - -Same compose file on every host — change `ALLOY_HOSTNAME` and `REMOTECFG_ID` per host. Filter in Grafana with `instance=~"..."`. - -## Security note - -Runs `privileged: true` + `network_mode: host`, matching the upstream Grafana Cloud docker integration. `network_mode: host` is required so `prometheus.exporter.unix` reports the host's real network interfaces (eth0…) instead of the alloy container's veth pair. If you need least-privilege, see the upstream Alloy docker integration docs and tighten capabilities. +Runs `privileged: true` with `network_mode: host`, matching the upstream +Grafana Cloud docker integration. Host networking is what lets +`prometheus.exporter.unix` report the host's real interfaces (`eth0`…) instead +of the container's veth pair. To tighten this, see the upstream Alloy docker +integration docs. ## Mounts | Mount | Why | |---|---| -| `/proc:/rootproc:ro` | node-exporter cpu/mem/load (referenced via `procfs_path`) | -| `/sys:/sys:ro` | node-exporter + cadvisor cgroups | -| `/:/rootfs:ro` | filesystem collector (referenced via `rootfs_path`) | +| `/proc:/rootproc:ro` | node-exporter cpu/mem/load, via `procfs_path` | +| `/sys:/sys:ro` | node-exporter and cadvisor cgroups | +| `/:/rootfs:ro` | filesystem collector, via `rootfs_path` | | `/dev/disk/:/dev/disk:ro` | node-exporter diskstats device labels | -| `/var/run/docker.sock` | `discovery.docker` + `loki.source.docker` | +| `/var/run/docker.sock` | `discovery.docker` and `loki.source.docker` | | `/var/lib/docker:ro` | cadvisor container metadata | -| `/var/log:/var/log:ro` | `loki.source.journal` (`/var/log/journal`) + `loki.source.file` (syslog/messages/*.log) | -| `/etc/machine-id:ro` | stable host id for the journal reader | -| `alloy-data` (named volume) | WAL + remotecfg cache | +| `/var/log:/var/log:ro` | `loki.source.journal` and `loki.source.file` | +| `/etc/machine-id:ro` | Stable host id for the journal reader | +| `alloy-data` | WAL and remotecfg cache | -## Design +## Notes -- [Upstream sources of truth](docs/upstream-sources-of-truth.md) — what we follow, what's in scope, how to audit dashboard metric needs. - -## Known noise (special cases) - -- [Coolify SSH session spam](docs/known-noise-coolify-ssh-sessions.md) — only relevant if Coolify manages the host. Safe to ignore otherwise. - -## License - -Apache 2.0 — see [LICENSE](LICENSE). +- [Upstream sources of truth](docs/upstream-sources-of-truth.md) — what this + follows, what is in scope, how to audit dashboard metric needs. +- [Coolify SSH session noise](docs/known-noise-coolify-ssh-sessions.md) — only + relevant on Coolify-managed hosts. diff --git a/couchbase/README.md b/couchbase/README.md index fa2d795..0dbcc39 100644 --- a/couchbase/README.md +++ b/couchbase/README.md @@ -1,2 +1,22 @@ -# couchbase-docker-compose -docker-compose.yml to run couchbase +# couchbase + +[Couchbase Server](https://www.couchbase.com), single node. + +Uses `docker-compose.yml`, publishes ports, and sets `restart: unless-stopped` +and `container_name: db` — unlike the platform-managed services described in +the [root README](../README.md). + +## Networking + +| Ports | Purpose | +| --- | --- | +| `8091-8097` | Cluster manager, views, query, search, analytics, eventing | +| `18091-18097` | The same, over TLS | +| `11207`, `11210` | Data service (TLS, plain) | +| `11280`, `9123` | Internal services | + +The admin console is on `8091`. Complete the first-run setup there. + +## Storage + +`couchbase_data` at `/opt/couchbase/var` — data, indexes, config, logs. diff --git a/gitea-mirror-local/README.md b/gitea-mirror-local/README.md index 8bea2da..98142ca 100644 --- a/gitea-mirror-local/README.md +++ b/gitea-mirror-local/README.md @@ -1,21 +1,21 @@ -# gitea-mirror-docker-compose +# gitea-mirror-local -A small Docker Compose stack that runs a self-hosted [Gitea](https://about.gitea.com/) -instance backed by PostgreSQL, alongside -[gitea-mirror](https://github.com/RayLabsHQ/gitea-mirror) for mirroring -repositories from GitHub into it. +Self-hosted [Gitea](https://about.gitea.com/) backed by PostgreSQL, with +[gitea-mirror](https://github.com/RayLabsHQ/gitea-mirror) mirroring GitHub +repositories into it. + +Publishes ports, unlike the platform-managed services described in the +[root README](../README.md) — but binds them all to `127.0.0.1`, so nothing is +reachable from outside the host. Put a reverse proxy in front for remote +access. ## Services -| Service | Image | Host address | -| -------------- | ----------------------------------------- | ------------------------ | -| `db` | `postgres:16-alpine` | internal only | -| `gitea` | `gitea/gitea:latest` | `127.0.0.1:3000` (HTTP) | -| | | `127.0.0.1:2222` (SSH) | -| `gitea-mirror` | `ghcr.io/raylabshq/gitea-mirror:latest` | `127.0.0.1:4321` | - -All published ports are bound to `127.0.0.1`, so nothing is reachable from -outside the host. Put a reverse proxy in front if you need remote access. +| Service | Image | Address | +| --- | --- | --- | +| `db` | `postgres:16-alpine` | internal only | +| `gitea` | `gitea/gitea:latest` | `127.0.0.1:3000` (HTTP), `127.0.0.1:2222` (SSH) | +| `gitea-mirror` | `ghcr.io/raylabshq/gitea-mirror:latest` | `127.0.0.1:4321` | `gitea` waits for `db` to pass its health check before starting. @@ -25,45 +25,31 @@ outside the host. Put a reverse proxy in front if you need remote access. docker compose up -d ``` -Then open to complete Gitea's first-run setup, and - to configure mirroring. +Complete Gitea's first-run setup at , then configure +mirroring at . -To clone over SSH, note that Gitea advertises port `2222`: +Gitea advertises SSH port `2222`: ```sh git clone ssh://git@127.0.0.1:2222//.git ``` -Stop the stack with `docker compose down`. State lives in named volumes -(`db-data`, `gitea-data`, `gitea-mirror-data`), so it survives a restart; add -`-v` to that command to delete it. - ## Configuration -`compose.yml` currently hardcodes its settings — database credentials, ports, -and the Gitea SSH port are written inline rather than read from the -environment. The stack runs as-is with no additional setup. +`compose.yml` hardcodes everything — database credentials, ports and the Gitea +SSH port are written inline and read no environment variables. `.env.example` +is not wired up: editing a `.env` has no effect until the compose file consumes +it via `env_file:` or `${VAR}` substitution. -`.env.example` documents the keys of a `.env` for this deployment, with sample -values. Copy it and fill in your own: +The Postgres credentials are `gitea` / `gitea`. Change them before exposing +this stack beyond localhost. -```sh -cp .env.example .env -``` +## Storage -The real `.env` is gitignored and never committed. Replace -`BETTER_AUTH_SECRET` with a freshly generated random value rather than the -sample string. +| Volume | Holds | +| --- | --- | +| `db-data` | PostgreSQL data | +| `gitea-data` | Repositories, Gitea config and state | +| `gitea-mirror-data` | Mirror job database | -Note that **`compose.yml` does not currently reference these variables**, so -editing `.env` has no effect until the compose file is wired up to consume it -(via `env_file:` or `${VAR}` substitution). The sample values also differ from -the hardcoded ones in a couple of places — for example `GITEA_SSH_PORT=8022` -versus the `2222` currently published by `compose.yml`. - -## Security notes - -- The Postgres credentials in `compose.yml` are the default `gitea` / `gitea`. - Change them before exposing this stack beyond localhost. -- `BETTER_AUTH_SECRET` in `.env.example` is a secret. Generate a fresh random - value per deployment and keep it out of version control. +`docker compose down -v` deletes all three. diff --git a/netdata/README.md b/netdata/README.md index da23b72..edcf248 100644 --- a/netdata/README.md +++ b/netdata/README.md @@ -1,36 +1,36 @@ -# netdata-docker-compose +# netdata -Docker Compose setup to run the [Netdata](https://www.netdata.cloud) monitoring agent. +[Netdata](https://www.netdata.cloud) monitoring agent. -## Quick start +Uses `docker-compose.yml`, and sets `restart: unless-stopped` and +`container_name: netdata` — unlike the platform-managed services described in +the [root README](../README.md). -```bash -docker compose up -d +Runs with `network_mode: host` and `pid: host`, plus `SYS_PTRACE`/`SYS_ADMIN` +and an unconfined AppArmor profile, so it can read host processes and metrics. +The dashboard is therefore on the host's own `19999`, not a published port. + +## Environment + +| Variable | Purpose | +| --- | --- | +| `NETDATA_CLAIM_TOKEN` | Netdata Cloud claim token. Without it the agent runs standalone. | + +```sh +NETDATA_CLAIM_TOKEN= docker compose up -d ``` -Access the dashboard at `http://localhost:19999`. +## Storage -## Customization +| Volume | Mount | Holds | +| --- | --- | --- | +| `netdataconfig` | `/etc/netdata` | Agent configuration | +| `netdatalib` | `/var/lib/netdata` | Metrics database, claim state | +| `netdatacache` | `/var/cache/netdata` | Cache | -Set your Netdata Cloud claim token to link the agent to your cloud account: - -```bash -NETDATA_CLAIM_TOKEN=your-token docker compose up -d -``` - -Key defaults you may want to override in `docker-compose.yml`: - -| Setting | Default | Notes | -|---------|---------|-------| -| Dashboard port | `19999` | Map to a different host port if needed | -| Volumes | `/proc`, `/sys`, host root | Required for full host metrics | +Host paths (`/`, `/proc`, `/sys`, `/var/log`, the Docker socket, dbus) are +mounted read-only for collection. ## Related -- [alloy](../alloy/README.md) — Grafana Alloy collector (ships metrics to Grafana Cloud) -- [grafana-git-sync](https://github.com/tiennm99/grafana-git-sync) — sync Grafana dashboards to git -- [ollama](../ollama/README.md) — Ollama LLM server compose setup - -## License - -Apache-2.0 — see [LICENSE](LICENSE). +- [alloy](../alloy/README.md) — ships metrics to Grafana Cloud instead diff --git a/ollama/README.md b/ollama/README.md index 4c8f1d2..8393bcd 100644 --- a/ollama/README.md +++ b/ollama/README.md @@ -1,32 +1,21 @@ -# ollama-docker-compose +# ollama -Minimal Docker Compose setup for running [Ollama](https://ollama.com) locally — exposes the API on `:11434` with a named volume so pulled models persist across restarts. +[Ollama](https://ollama.com) LLM server. CPU-only. -> The "optionally with a web UI" phrase from the description is **not yet wired**. Open Web UI sidecar can be added — see [Roadmap](#roadmap). +Uses `docker-compose.yml`, publishes `11434`, and sets +`restart: unless-stopped` and `container_name: ollama` — unlike the +platform-managed services described in the [root README](../README.md). -## Quick start +## Usage -```bash -docker compose up -d -``` - -Ollama API → `http://localhost:11434`. - -Pull a model: - -```bash +```sh docker compose exec ollama ollama pull llama3.2 -``` - -Chat from CLI: - -```bash docker compose exec ollama ollama run llama3.2 ``` -Smoke test via API: +The API is on `http://localhost:11434`: -```bash +```sh curl http://localhost:11434/api/generate -d '{ "model": "llama3.2", "prompt": "Why is the sky blue?", @@ -34,23 +23,9 @@ curl http://localhost:11434/api/generate -d '{ }' ``` -## What's inside +For GPU, add `deploy.resources.reservations.devices` for the NVIDIA runtime — +see the [Ollama Docker docs](https://hub.docker.com/r/ollama/ollama). -| Field | Value | -|---|---| -| Image | `ollama/ollama` (latest) | -| Port | `11434:11434` | -| Volume | `ollama:/root/.ollama` (models, manifests) | -| Restart policy | `unless-stopped` | +## Storage -## GPU - -To enable GPU, add `deploy.resources.reservations.devices` for the NVIDIA runtime — see the [Ollama Docker docs](https://hub.docker.com/r/ollama/ollama). CPU-only by default. - -## Roadmap - -- Add Open Web UI sidecar (`ghcr.io/open-webui/open-webui:main`) on `:3000` wired to this Ollama instance. - -## License - -Apache-2.0 +`ollama` at `/root/.ollama` — pulled models and manifests. diff --git a/openvpn-as/README.md b/openvpn-as/README.md index 8e69e0b..33cc2cd 100644 --- a/openvpn-as/README.md +++ b/openvpn-as/README.md @@ -1,2 +1,25 @@ -# openvpn-as-docker-compose -Run OpenVPN Access Server with Docker compose +# openvpn-as + +[OpenVPN Access Server](https://openvpn.net/access-server/). + +Uses `docker-compose.yml`, publishes ports, and sets `restart: unless-stopped` +and `container_name: openvpn-as` — unlike the platform-managed services +described in the [root README](../README.md). + +Needs `/dev/net/tun` plus the `MKNOD` and `NET_ADMIN` capabilities to create +the VPN interface. + +## Networking + +| Port | Purpose | +| --- | --- | +| `943` | Admin and client web UI | +| `443` | OpenVPN over TCP | +| `1194/udp` | OpenVPN over UDP | + +Admin UI at `https://:943/admin`. The initial credentials are printed to +the container logs on first start. + +## Storage + +`openvpn-as-data` at `/openvpn` — config, certificates, user database. diff --git a/paseo/.env.example b/paseo/.env.example index bb1109b..1c6c21b 100644 --- a/paseo/.env.example +++ b/paseo/.env.example @@ -2,8 +2,8 @@ # # cp .env.example .env -# Password for the daemon API and web UI. Doubles as the paseo user's login -# password, so it is also what `sudo` asks for inside a terminal. +# Password for the daemon API and web UI. Also the paseo user's login password, +# so it is what `sudo` asks for inside a terminal. # Generate one with: openssl rand -base64 24 PASEO_PASSWORD= @@ -11,27 +11,24 @@ PASEO_PASSWORD= # Coolify/Dokploy must be listed here. IPs and localhost are always allowed. PASEO_HOSTNAMES= -# Proxy addresses whose X-Forwarded-Proto the daemon trusts. Without this the -# daemon only trusts loopback, so behind Coolify/Dokploy it sees plain HTTP, -# tells the web UI the connection is not TLS, and the UI falls back to trying -# ws://...:6767 from an HTTPS page -- which the browser blocks. +# Proxy source IPs whose X-Forwarded-Proto the daemon trusts. Without this it +# trusts loopback only, reads the request as plain HTTP behind Coolify/Dokploy, +# and tells the web UI to open ws:// from an HTTPS page -- which browsers block. # `uniquelocal` covers the private ranges Docker bridge networks use. PASEO_TRUSTED_PROXIES=uniquelocal -# Container hostname. Paseo shows it as the host label in the web UI, so -# without it you get a random container ID. +# Container hostname, shown as the host label in the web UI. Without it the +# label is a random container ID. PASEO_LABEL=paseo # Timezone for logs and agent shells. TZ=Asia/Ho_Chi_Minh -# Git identity for agents and terminals, as author and committer. These reach -# git as environment variables, so they need no `git config` and survive a -# rebuild -- unlike a ~/.gitconfig, which only lives in the /home/paseo volume. +# Git identity for agents and terminals, as author and committer. git reads +# these directly, so no `git config` step is needed. GIT_NAME=tiennm99 GIT_EMAIL=tiennm99@outlook.com -# Shell for Paseo's terminals. Paseo reads $SHELL and falls back to /bin/sh -# (dash), ignoring the user's login shell, so set it here rather than running -# chsh -- which would be reverted by the next rebuild anyway. +# Shell for Paseo's terminals. Paseo reads $SHELL and otherwise falls back to +# /bin/sh (dash); it ignores the user's login shell, so `chsh` has no effect. SHELL=/bin/zsh diff --git a/paseo/README.md b/paseo/README.md index 67d54f0..e9057c8 100644 --- a/paseo/README.md +++ b/paseo/README.md @@ -9,23 +9,23 @@ Python, and shell tooling to the 1. Set the variables from `.env.example` in Coolify or Dokploy. 2. Point the domain at port `6767` and deploy. -3. Open the domain. At the pairing screen enter the host **with the port**: +3. Open the domain. At the pairing screen enter the host **with the port**, + then the `PASEO_PASSWORD` value: ``` paseo.example.com:443 ``` - Then the `PASEO_PASSWORD` value. -4. In a terminal inside Paseo, log in once per tool you use — see the - [agents](#agents) table — plus `gh auth login`. +4. In a terminal inside Paseo, log in once per tool you use — see + [Agents](#agents) — plus `gh auth login`. -The port is required — the UI rejects a bare hostname. You must type the -address yourself: the daemon builds its auto-connect hint from the `Host` -header, browsers drop the default `:443`, and the UI discards a hint with no -port. It then shows its built-in `localhost:6767` placeholder, which in a -browser means your own machine. +The port must be typed by hand. The UI rejects a bare hostname, and the +auto-connect hint does not help: the daemon builds it from the `Host` header, +browsers drop the default `:443`, and the UI discards a hint with no port. What +you see instead is its `localhost:6767` placeholder, which in a browser means +your own machine. -Still stuck on `localhost:6767` after entering the address? Clear site data — +If it stays on `localhost:6767` after you enter the address, clear site data — the old entry is cached in `localStorage`. ## Environment @@ -35,18 +35,18 @@ the old entry is cached in `localStorage`. | `PASEO_PASSWORD` | Web UI and API login, and the `paseo` user's `sudo` password. Generate with `openssl rand -base64 24`. | | `PASEO_HOSTNAMES` | Domains allowed to reach the daemon, comma-separated. Your domain must be listed. | | `PASEO_TRUSTED_PROXIES` | Set to `uniquelocal`, or the UI loads but never connects. | -| `PASEO_LABEL` | Container hostname. Paseo shows it as the host label in the UI; without it you get a random container ID. | +| `PASEO_LABEL` | Container hostname, shown as the host label in the UI. Without it the label is a random container ID. | | `GIT_NAME` / `GIT_EMAIL` | Git author and committer identity for agents and terminals. | | `TZ` | Timezone for logs and agent shells. | | `SHELL` | Shell for Paseo's terminals. Paseo reads `$SHELL` and falls back to `/bin/sh`, ignoring the login shell, so `chsh` has no effect. | -`PASEO_TRUSTED_PROXIES` matches the *source IP* of the proxy, so hostnames are -rejected. By default the daemon believes `X-Forwarded-Proto` only from -loopback, but Coolify's Traefik reaches it from the Docker bridge network. It -therefore reads the request as plain HTTP, tells the UI to use `ws://` on an -`https://` page, and the browser blocks that as mixed content. `uniquelocal` -covers the private ranges Docker uses; an exact CIDR works too, but Coolify -assigns a fresh subnet per project. +`PASEO_TRUSTED_PROXIES` matches the proxy's *source IP*, so hostnames are +rejected. The daemon trusts `X-Forwarded-Proto` from loopback only, but +Coolify's Traefik reaches it from the Docker bridge network — so it reads the +request as plain HTTP, tells the UI to use `ws://` on an `https://` page, and +the browser blocks that as mixed content. `uniquelocal` covers the private +ranges Docker uses. An exact CIDR works too, but Coolify assigns a fresh subnet +per project. ## Networking @@ -56,9 +56,6 @@ Listens on `6767`, published nowhere — the platform maps the domain to it, so ## Agents -All six come from npm, so new providers are one more entry on the `npm -install` line. - | Agent | Command | Package | Log in with | | --- | --- | --- | --- | | Claude Code | `claude` | `@anthropic-ai/claude-code` | `claude` | @@ -68,8 +65,9 @@ install` line. | Pi | `pi` | `@earendil-works/pi-coding-agent` | `pi` | | Oh My Pi | `omp` | `@oh-my-pi/pi-coding-agent` | `omp` | -Pi and Oh My Pi are separate projects sharing an ancestor, so they install side -by side and their commands do not collide. +All six come from npm, so another provider is one more entry on the `npm +install` line. Pi and Oh My Pi are separate projects sharing an ancestor; their +commands do not collide. ## Storage @@ -79,10 +77,10 @@ by side and their commands do not collide. | `paseo-workspace` | `/workspace` | Code the agents work on | Every agent CLI and `gh` keep their config under `/home/paseo`, so all logins -survive a redeploy. The base image already points `CLAUDE_CONFIG_DIR`, -`CODEX_HOME` and the `XDG_*` variables (which the rest follow) into it. -Dotfiles live there too — a `.zshrc` or oh-my-zsh install persists, but -anything written outside `$HOME` (`chsh`, `apt install`) is lost on rebuild. +survive a redeploy — the base image points `CLAUDE_CONFIG_DIR`, `CODEX_HOME` +and the `XDG_*` variables into it. Dotfiles live there too, so a `.zshrc` or +oh-my-zsh install persists. Anything written outside `$HOME` (`chsh`, +`apt install`) is lost on rebuild. ## Image @@ -96,28 +94,25 @@ anything written outside `$HOME` (`chsh`, `apt install`) is lost on rebuild. | `less nano jq unzip zip lsof psmisc ugrep bfs zsh sudo` | apt | — | Bump a language with a build arg, e.g. `--build-arg GO_VERSION=1.27.1`. `uv` -itself is installed too. Notes for anyone tempted to change things: +itself is installed too. -- npm installs the same native binaries the vendors' own installers do — each - package is a thin launcher with the real binary as a per-platform optional - dependency. Don't swap in `curl | bash`: those write to `$HOME/.local`, and - `$HOME` is `/home/paseo`, a volume mount that hides anything baked in at - build time. -- Bun is there for `omp` alone. That package is Bun-compiled and its bin opens - with `#!/usr/bin/env bun`, so dropping Bun leaves an `omp` that won't start. - Nothing else in the image uses it — Node still runs the other five. -- The agent CLIs can't auto-update (`paseo` can't write `/usr/local`), so +Constraints worth knowing before changing the `Dockerfile`: + +- `$HOME` is `/home/paseo`, a volume that masks anything the build writes + there. Hence `/opt/python` and `/usr/local/go` rather than the defaults, and + hence npm rather than each vendor's `curl | bash` installer, which writes to + `$HOME/.local`. npm delivers the same native binaries regardless — every one + of these packages is a thin launcher with the real binary as a per-platform + optional dependency. +- Bun exists for `omp` alone, whose bin opens with `#!/usr/bin/env bun`. + Node runs the other five. +- The agent CLIs cannot auto-update, since `paseo` cannot write `/usr/local`. Claude Code shows a notice at startup. Rebuild to update. -- The image stays root on purpose: the entrypoint chowns the volumes, then - drops to the `paseo` user (uid 1000) with `gosu`. -- `sudo` prompts for `PASEO_PASSWORD`. `entrypoint.sh` wraps the image's own - entrypoint to set it — it has to run at start, since baking a password into a - layer would commit the secret and `/etc/shadow` is not on a volume, so it - reverts on every recreate. Leave `PASEO_PASSWORD` empty and the account stays - locked, which means no `sudo`. -- `sudo` resets `PATH` to its `secure_path`, which does not include - `/usr/local/go/bin`. `sudo go ...` therefore fails; use `sudo env PATH="$PATH" - go ...` or the full path. -- Nothing installs into `$HOME`. That is `/home/paseo`, a volume mount that - hides anything baked in at build time — hence `/opt/python` and - `/usr/local/go` rather than the defaults. +- The image stays root: the entrypoint chowns the volumes, then drops to the + `paseo` user (uid 1000) with `gosu`. +- `entrypoint.sh` sets the `paseo` password from `PASEO_PASSWORD` on every + start, while still root. `/etc/shadow` is not on a volume, so it reverts on + each recreate. An empty `PASEO_PASSWORD` leaves the account locked and `sudo` + unusable. +- `sudo` resets `PATH` to its `secure_path`, which excludes + `/usr/local/go/bin`. Use `sudo env PATH="$PATH" go ...` or the full path. diff --git a/paseo/entrypoint.sh b/paseo/entrypoint.sh index 2711c21..f5680bb 100755 --- a/paseo/entrypoint.sh +++ b/paseo/entrypoint.sh @@ -2,12 +2,11 @@ # Sets the paseo user's login password from PASEO_PASSWORD, then hands off to # the image's own entrypoint. # -# Has to happen at start, not build: the password is a secret and would -# otherwise be baked into a layer. Has to happen here, not in a later hook: the -# base entrypoint ends in `exec gosu paseo`, so it never comes back, and by then -# we are no longer root and cannot write /etc/shadow. And it has to run every -# time -- /etc/shadow sits in the image, not in the /home/paseo volume, so it -# reverts on every container recreate. +# At start rather than at build, so the password never lands in a layer. Here +# rather than after the base entrypoint, which ends in `exec gosu paseo` and so +# never returns, and by then is no longer root. Every start, because +# /etc/shadow is in the image, not the /home/paseo volume, and reverts on each +# container recreate. set -euo pipefail if [[ "$(id -u)" == "0" && -n "${PASEO_PASSWORD:-}" ]]; then diff --git a/tastyigniter/README.md b/tastyigniter/README.md index 807c429..e05d404 100644 --- a/tastyigniter/README.md +++ b/tastyigniter/README.md @@ -1,130 +1,50 @@ -# TastyIgniter Docker Compose +# tastyigniter -This repository contains a Docker Compose setup for TastyIgniter, making it easy to deploy on Coolify or any other Docker-compatible platform. +[TastyIgniter](https://tastyigniter.com) restaurant ordering platform, built +from the local `Dockerfile` (PHP 8.3 FPM) and served by nginx. -## Features +Uses `docker-compose.yml`, publishes `${PORT:-80}`, and sets `restart:` and +`container_name:` on every service — unlike the platform-managed services +described in the [root README](../README.md). -- PHP 8.3 with FPM -- Nginx web server -- MySQL 8 database -- Queue worker for background jobs -- Cron job for scheduled tasks -- Persistent volumes for data storage -- Health checks for all services -- Environment variable configuration +## Services -## Prerequisites +| Service | Image | Role | +| --- | --- | --- | +| `app` | local `Dockerfile` | PHP-FPM application | +| `nginx` | `nginx:alpine` | Web server, published on `${PORT:-80}` | +| `db` | `mysql:8` | Database | +| `queue` | local `Dockerfile` | `artisan queue:work` for background jobs | +| `cron` | local `Dockerfile` | `artisan schedule:run` every 60s | -- Docker -- Docker Compose -- Coolify (for deployment) +All five have health checks; `app`, `queue` and `cron` wait on `db`. -## Quick Start +## Environment -1. Clone this repository: -```bash -git clone https://github.com/yourusername/tastyigniter-docker-compose.git -cd tastyigniter-docker-compose +| Variable | Purpose | +| --- | --- | +| `APP_KEY` | Laravel application key, 32 random characters. Required. | +| `APP_URL` | Public URL. Required. | +| `APP_NAME` / `APP_ENV` / `APP_DEBUG` | Defaults `TastyIgniter` / `production` / `false`. | +| `IGNITER_CARTE_KEY` | TastyIgniter Carte key, if used. | +| `IGNITER_LOCATION_MODE` | `single` or `multiple`. Defaults to `multiple`. | +| `DB_DATABASE` / `DB_USERNAME` / `DB_PASSWORD` | Database credentials. Defaults `tastyigniter` / `tastyigniter` / unset. | +| `DB_PREFIX` | Table prefix. Defaults to `ti_`. | +| `MYSQL_ROOT_PASSWORD` | MySQL root password. Required. | +| `MAIL_MAILER` / `MAIL_FROM_ADDRESS` / `MAIL_FROM_NAME` | Mail delivery. Defaults to the `log` driver. | +| `PORT` | Host port for nginx. Defaults to `80`. | +| `STACK_NAME` | Prefix for volume and network names. Defaults to `tastyigniter`. | + +## Storage + +| Volume | Holds | +| --- | --- | +| `${STACK_NAME}_dbdata` | MySQL data | +| `${STACK_NAME}_app` | Application files | +| `${STACK_NAME}_storage` | Uploads, cache, logs | + +Back up the database with: + +```sh +docker compose exec db mysqldump -u root -p tastyigniter > backup.sql ``` - -2. Copy the example environment file: -```bash -cp .env.example .env -``` - -3. Edit the `.env` file with your configuration: -```bash -nano .env -``` - -4. Start the containers: -```bash -docker-compose up -d -``` - -## Deployment on Coolify - -1. Push this repository to your Git provider (GitHub, GitLab, etc.) - -2. In Coolify: - - Create a new service - - Select "Docker Compose" as the deployment method - - Connect your Git repository - - Set the following environment variables from the `.env.example` file - - Deploy the service - -### Required Environment Variables - -Make sure to set these environment variables in Coolify: - -- `APP_KEY`: Generate a random 32-character string -- `APP_URL`: Your domain name -- `DB_PASSWORD`: Secure database password -- `MYSQL_ROOT_PASSWORD`: Secure root password -- `IGNITER_CARTE_KEY`: Your TastyIgniter Carte key (if using) - -### Volume Management - -The following volumes are automatically managed by Coolify: -- `dbdata`: MySQL database data -- `tastyigniter`: Application files -- `tastyigniter-storage`: Application storage - -### Health Checks - -The deployment includes health checks for: -- Application container (PHP-FPM) -- Nginx web server -- MySQL database - -## Development - -To run in development mode: - -1. Set `APP_ENV=local` in your `.env` file -2. Set `APP_DEBUG=true` in your `.env` file -3. Run `docker-compose up -d` - -## Maintenance - -### Database Backup - -To backup the database: -```bash -docker-compose exec db mysqldump -u root -p tastyigniter > backup.sql -``` - -### Logs - -View logs for all services: -```bash -docker-compose logs -f -``` - -View logs for a specific service: -```bash -docker-compose logs -f app -docker-compose logs -f nginx -docker-compose logs -f db -``` - -## Troubleshooting - -1. If the application fails to start: - - Check the logs: `docker-compose logs -f app` - - Verify environment variables are set correctly - - Ensure all required ports are available - -2. If the database connection fails: - - Check the database logs: `docker-compose logs -f db` - - Verify database credentials in `.env` - - Ensure the database container is running: `docker-compose ps` - -3. If the web server is not accessible: - - Check nginx logs: `docker-compose logs -f nginx` - - Verify port configuration in `.env` - - Check if the domain is properly configured - -## License - -This project is licensed under the MIT License - see the LICENSE file for details. diff --git a/traffmonetizer/.env.example b/traffmonetizer/.env.example new file mode 100644 index 0000000..090ba03 --- /dev/null +++ b/traffmonetizer/.env.example @@ -0,0 +1,6 @@ +# Copy to .env and fill in. Never commit .env. +# +# cp .env.example .env + +# Application token from the TraffMonetizer dashboard, under "Your token". +TOKEN= diff --git a/traffmonetizer/README.md b/traffmonetizer/README.md index c6da18e..63cdd56 100644 --- a/traffmonetizer/README.md +++ b/traffmonetizer/README.md @@ -1,2 +1,20 @@ -# traffmonetizer-docker-compose -Sample docker-compose.yml for TraffMonetizer +# traffmonetizer + +[TraffMonetizer](https://traffmonetizer.com) bandwidth-sharing client. + +Uses `docker-compose.yml`, and sets `restart: always` and +`container_name: tm` — unlike the platform-managed services described in the +[root README](../README.md). Publishes no ports; the client only makes outbound +connections. + +The image tag is `arm64v8`. Change it to match the host architecture. + +## Environment + +| Variable | Purpose | +| --- | --- | +| `TOKEN` | Application token from the TraffMonetizer dashboard. Passed both as an environment variable and on the `start accept --token` command line. | + +## Storage + +None — the client keeps no state worth persisting.