Compare commits

...
4 Commits
Author SHA1 Message Date
tiennm99 aa02fd2f5b chore: drop the openvpn-as and tastyigniter services
Remove both directories and their rows from the services table.
2026-09-16 20:57:04 +07:00
tiennm99 23b9bf8bf6 docs: describe current state only, and fill in the stub READMEs
Drop the history and roadmap asides: which services predate the collection's
conventions, the unwired Open Web UI plan, the generic clone-and-troubleshoot
boilerplate. Services that publish ports or set `restart:` now simply say so.

couchbase, openvpn-as and traffmonetizer had two-line READMEs; give them the
ports, variables and storage the root README promises. traffmonetizer reads
${TOKEN} and had no .env.example, so add one.
2026-09-16 20:45:18 +07:00
tiennm99 04ccc93969 feat(paseo): add Copilot, Pi and Oh My Pi agent CLIs
Six agents now ship in the image. All come from npm; omp additionally needs
Bun, since its bin is Bun-compiled and opens with `#!/usr/bin/env bun`.
BUN_INSTALL puts Bun in /usr/local, clear of the paseo-home volume.
2026-09-16 20:40:39 +07:00
tiennm99 65ec872e62 feat(paseo): add Codex and opencode-ai agent CLIs 2026-09-16 20:35:00 +07:00
24 changed files with 299 additions and 1297 deletions

No files matched your search

+16 -20
View File
@@ -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 <service>
@@ -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,13 @@ 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 |
| [openvpn-as](openvpn-as/README.md) | OpenVPN Access Server |
| [ollama](ollama/README.md) | Ollama LLM 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).
+63 -50
View File
@@ -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-<region>.grafana.net/api/prom/push
export PROM_USER=<prometheus-user-id>
export LOKI_URL=https://logs-prod-XXX.grafana.net/loki/api/v1/push
export LOKI_USER=<loki-user-id>
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.
+22 -2
View File
@@ -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.
+30 -44
View File
@@ -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 <http://127.0.0.1:3000> to complete Gitea's first-run setup, and
<http://127.0.0.1:4321> to configure mirroring.
Complete Gitea's first-run setup at <http://127.0.0.1:3000>, then configure
mirroring at <http://127.0.0.1:4321>.
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/<owner>/<repo>.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.
+26 -26
View File
@@ -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=<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
+13 -38
View File
@@ -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.
-201
View File
@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-2
View File
@@ -1,2 +0,0 @@
# openvpn-as-docker-compose
Run OpenVPN Access Server with Docker compose
-21
View File
@@ -1,21 +0,0 @@
version: '3'
services:
openvpn-as:
image: openvpn/openvpn-as
container_name: openvpn-as
devices:
- /dev/net/tun
cap_add:
- MKNOD
- NET_ADMIN
ports:
- 943:943
- 443:443
- 1194:1194/udp
volumes:
- openvpn-as-data:/openvpn
restart: unless-stopped
volumes:
openvpn-as-data:
+11 -14
View File
@@ -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
+22 -4
View File
@@ -46,11 +46,29 @@ RUN install -d -m 0755 /etc/apt/keyrings \
&& apt-get install -y --no-install-recommends gh \
&& rm -rf /var/lib/apt/lists/*
# --- bun -------------------------------------------------------------------
# Only omp needs it: @oh-my-pi/pi-coding-agent is Bun-compiled and its bin
# opens with `#!/usr/bin/env bun`, so without Bun on PATH npm installs an omp
# that cannot start. BUN_INSTALL lands it in /usr/local, not the $HOME/.bun
# default that the paseo-home volume would mask.
ENV BUN_INSTALL=/usr/local
RUN curl -fsSL https://bun.sh/install | bash
# --- agent CLIs ------------------------------------------------------------
# npm here delivers the same native binary as Anthropic's standalone installer;
# it is not a Node wrapper. Do not swap it for the `curl | bash` installer,
# which writes to $HOME/.local. Last because it changes most often.
RUN npm install -g @anthropic-ai/claude-code
# These all publish the same way: a thin launcher plus the real binary as a
# per-platform optional dependency. npm therefore installs exactly what each
# vendor's own installer would, and is not a Node wrapper.
#
# Do not swap any of them for their `curl | bash` installer -- those write to
# $HOME/.local, and $HOME is /home/paseo, a volume that masks whatever the
# build put there. Last because these change most often.
RUN npm install -g \
@anthropic-ai/claude-code \
@openai/codex \
opencode-ai \
@github/copilot \
@earendil-works/pi-coding-agent \
@oh-my-pi/pi-coding-agent
# --- entrypoint ------------------------------------------------------------
# Wraps the image's entrypoint to set the paseo user's password while still
+65 -50
View File
@@ -1,30 +1,31 @@
# paseo
[Paseo](https://paseo.sh) — self-hosted daemon and web UI for running coding
agents. Built from a local `Dockerfile` that adds Claude Code, `gh`, Go,
Python, and shell tooling to the [official image](https://paseo.sh/docs/docker),
which ships none of it.
agents. Built from a local `Dockerfile` that adds six agent CLIs, `gh`, Go,
Python, and shell tooling to the
[official image](https://paseo.sh/docs/docker), which ships none of it.
## Setup
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: `claude` and `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
@@ -34,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
@@ -53,51 +54,65 @@ Listens on `6767`, published nowhere — the platform maps the domain to it, so
`localhost:6767` on the host refuses connections. See the
[root README](../README.md) for why.
## Agents
| Agent | Command | Package | Log in with |
| --- | --- | --- | --- |
| Claude Code | `claude` | `@anthropic-ai/claude-code` | `claude` |
| Codex | `codex` | `@openai/codex` | `codex login` |
| opencode | `opencode` | `opencode-ai` | `opencode auth login` |
| Copilot CLI | `copilot` | `@github/copilot` | `/login` inside `copilot` |
| Pi | `pi` | `@earendil-works/pi-coding-agent` | `pi` |
| Oh My Pi | `omp` | `@oh-my-pi/pi-coding-agent` | `omp` |
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
| Volume | Mount | Holds |
| --- | --- | --- |
| `paseo-home` | `/home/paseo` | Daemon state, agent configs, credentials (`.claude`, `.codex`) |
| `paseo-home` | `/home/paseo` | Daemon state, agent configs, credentials (`.claude`, `.codex`, `.config/*`) |
| `paseo-workspace` | `/workspace` | Code the agents work on |
Claude Code and `gh` keep their config in `/home/paseo`, so both logins survive
a redeploy. Dotfiles live there too — a `.zshrc` or oh-my-zsh install persists,
but anything written outside `$HOME` (`chsh`, `apt install`) is lost on rebuild.
Every agent CLI and `gh` keep their config under `/home/paseo`, so all logins
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
| Tool | Source | Why not apt |
| --- | --- | --- |
| Claude Code | npm | — |
| Agent CLIs (see [above](#agents)) | npm | — |
| Bun | Official `bun.sh` install script | Debian does not package it |
| `gh` | GitHub's signed apt repo | Debian does not package it |
| Go (`GO_VERSION`) | Official go.dev tarball | Debian 12 ships 1.19 |
| Python (`PYTHON_VERSION`) | `uv python install` | Debian 12 ships 3.11 |
| `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`. Add
other agent providers to the `npm install` line:
Bump a language with a build arg, e.g. `--build-arg GO_VERSION=1.27.1`. `uv`
itself is installed too.
```dockerfile
RUN npm install -g @anthropic-ai/claude-code @openai/codex opencode-ai
```
Constraints worth knowing before changing the `Dockerfile`:
`uv` itself is installed too. Notes for anyone tempted to change things:
- npm installs the same native binary as Anthropic's standalone installer.
Don't swap in `curl | bash` — it writes to `$HOME/.local`, and `$HOME` is
`/home/paseo`, a volume mount that hides anything baked in at build time.
- Claude Code can't auto-update (`paseo` can't write `/usr/local`), so it 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.
- `$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: 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.
+5 -6
View File
@@ -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
-26
View File
@@ -1,26 +0,0 @@
# Application
APP_NAME=TastyIgniter
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=https://your-domain.com
# TastyIgniter specific
IGNITER_LOCATION_MODE=multiple
IGNITER_CARTE_KEY=
# Database
DB_DATABASE=tastyigniter
DB_USERNAME=tastyigniter
DB_PASSWORD=your_secure_password
DB_PREFIX=ti_
MYSQL_ROOT_PASSWORD=your_secure_root_password
# Mail
MAIL_MAILER=log
MAIL_FROM_ADDRESS=noreply@your-domain.com
MAIL_FROM_NAME=TastyIgniter
# Deployment
PORT=80
STACK_NAME=tastyigniter
-64
View File
@@ -1,64 +0,0 @@
FROM php:8.3-fpm
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
libzip-dev \
libicu-dev \
zip \
unzip \
default-mysql-client \
libfreetype6-dev \
libjpeg62-turbo-dev \
libpng-dev \
libwebp-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
&& docker-php-ext-install \
pdo_mysql \
mbstring \
exif \
pcntl \
bcmath \
gd \
zip \
xml \
intl \
ctype \
tokenizer \
opcache
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Set working directory
WORKDIR /var/www/html
# Instead of running composer create-project during build, we'll do it in the entrypoint
# This allows us to pass the --ignore-platform-req flag if needed
# Copy custom entrypoint
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
# Set file permissions for the eventual app directory
RUN mkdir -p /var/www/html/storage && \
chown -R www-data:www-data /var/www/html && \
chmod -R 755 /var/www/html
# Configure PHP
COPY docker/php/php.ini /usr/local/etc/php/conf.d/custom.ini
COPY docker/php/www.conf /usr/local/etc/php-fpm.d/www.conf
# Entrypoint script
ENTRYPOINT ["entrypoint.sh"]
# Start PHP-FPM server
CMD ["php-fpm"]
-201
View File
@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-130
View File
@@ -1,130 +0,0 @@
# TastyIgniter Docker Compose
This repository contains a Docker Compose setup for TastyIgniter, making it easy to deploy on Coolify or any other Docker-compatible platform.
## Features
- 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
## Prerequisites
- Docker
- Docker Compose
- Coolify (for deployment)
## Quick Start
1. Clone this repository:
```bash
git clone https://github.com/yourusername/tastyigniter-docker-compose.git
cd tastyigniter-docker-compose
```
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.
-133
View File
@@ -1,133 +0,0 @@
version: '3'
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: tastyigniter-app
restart: unless-stopped
environment:
- APP_NAME=${APP_NAME:-TastyIgniter}
- APP_ENV=${APP_ENV:-production}
- APP_KEY=${APP_KEY}
- APP_DEBUG=${APP_DEBUG:-false}
- APP_URL=${APP_URL}
- IGNITER_LOCATION_MODE=${IGNITER_LOCATION_MODE:-multiple}
- IGNITER_CARTE_KEY=${IGNITER_CARTE_KEY}
# Database configuration
- DB_CONNECTION=mysql
- DB_HOST=db
- DB_PORT=3306
- DB_DATABASE=${DB_DATABASE:-tastyigniter}
- DB_USERNAME=${DB_USERNAME:-tastyigniter}
- DB_PASSWORD=${DB_PASSWORD}
- DB_PREFIX=${DB_PREFIX:-ti_}
# Cache and Session
- BROADCAST_DRIVER=log
- CACHE_DRIVER=file
- QUEUE_CONNECTION=database
- SESSION_DRIVER=file
- SESSION_LIFETIME=120
# Mail Configuration
- MAIL_MAILER=${MAIL_MAILER:-log}
- MAIL_FROM_ADDRESS=${MAIL_FROM_ADDRESS:-noreply@tastyigniter.tld}
- MAIL_FROM_NAME=${MAIL_FROM_NAME:-TastyIgniter}
volumes:
- tastyigniter:/var/www/html
- tastyigniter-storage:/var/www/html/storage
depends_on:
db:
condition: service_healthy
networks:
- tastyigniter-network
healthcheck:
test: ["CMD", "php", "artisan", "--version"]
interval: 30s
timeout: 10s
retries: 3
nginx:
image: nginx:alpine
container_name: tastyigniter-nginx
restart: unless-stopped
ports:
- "${PORT:-80}:80"
volumes:
- tastyigniter:/var/www/html
- ./docker/nginx:/etc/nginx/conf.d
depends_on:
app:
condition: service_healthy
networks:
- tastyigniter-network
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:80"]
interval: 30s
timeout: 10s
retries: 3
db:
image: mysql:8
container_name: tastyigniter-db
restart: unless-stopped
environment:
- MYSQL_DATABASE=${DB_DATABASE:-tastyigniter}
- MYSQL_USER=${DB_USERNAME:-tastyigniter}
- MYSQL_PASSWORD=${DB_PASSWORD}
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
volumes:
- dbdata:/var/lib/mysql
networks:
- tastyigniter-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
interval: 30s
timeout: 10s
retries: 3
cron:
build:
context: .
dockerfile: Dockerfile
container_name: tastyigniter-cron
restart: unless-stopped
command: bash -c "while true; do php /var/www/html/artisan schedule:run --verbose --no-interaction; sleep 60; done"
volumes:
- tastyigniter:/var/www/html
depends_on:
app:
condition: service_healthy
networks:
- tastyigniter-network
queue:
build:
context: .
dockerfile: Dockerfile
container_name: tastyigniter-queue
restart: unless-stopped
command: php /var/www/html/artisan queue:work
volumes:
- tastyigniter:/var/www/html
depends_on:
app:
condition: service_healthy
networks:
- tastyigniter-network
volumes:
dbdata:
name: ${STACK_NAME:-tastyigniter}_dbdata
tastyigniter:
name: ${STACK_NAME:-tastyigniter}_app
tastyigniter-storage:
name: ${STACK_NAME:-tastyigniter}_storage
networks:
tastyigniter-network:
name: ${STACK_NAME:-tastyigniter}_network
driver: bridge
-115
View File
@@ -1,115 +0,0 @@
#!/bin/bash
set -e
# Function to log messages
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}
# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Function to validate environment variables
validate_env() {
local required_vars=("DB_HOST" "DB_DATABASE" "DB_USERNAME" "DB_PASSWORD")
local missing_vars=()
for var in "${required_vars[@]}"; do
if [ -z "${!var}" ]; then
missing_vars+=("$var")
fi
done
if [ ${#missing_vars[@]} -ne 0 ]; then
log "ERROR: Missing required environment variables: ${missing_vars[*]}"
exit 1
fi
}
# Create log directory if it doesn't exist
mkdir -p /var/log/php
chown -R www-data:www-data /var/log/php
# Validate environment variables
validate_env
# Check if TastyIgniter is already installed
if [ ! -f /var/www/html/composer.json ] || [ ! -d /var/www/html/vendor ]; then
log "Installing TastyIgniter..."
# Create new TastyIgniter project
composer create-project tastyigniter/tastyigniter:^v4.0 /tmp/tastyigniter --prefer-dist --no-interaction --no-progress
# Move files to web root
cp -a /tmp/tastyigniter/. /var/www/html/
rm -rf /tmp/tastyigniter
log "TastyIgniter files installed"
fi
# Set proper permissions
log "Setting proper permissions..."
chown -R www-data:www-data /var/www/html
chmod -R 755 /var/www/html
chmod -R 775 /var/www/html/storage
chmod -R 775 /var/www/html/bootstrap/cache
# Generate app key if not set
if [ -z "$APP_KEY" ]; then
log "Generating application key..."
php artisan key:generate
fi
# Wait for database to be ready
log "Waiting for database to be ready..."
max_tries=30
counter=1
while ! mysql -h "$DB_HOST" -u "$DB_USERNAME" -p"$DB_PASSWORD" -e "SELECT 1" >/dev/null 2>&1; do
if [ $counter -ge $max_tries ]; then
log "ERROR: Database connection failed after $max_tries attempts"
exit 1
fi
log "Database connection attempt $counter/$max_tries failed, retrying in 2 seconds..."
sleep 2
counter=$((counter + 1))
done
log "Database connection established"
# Run TastyIgniter installer in non-interactive mode if not installed
if [ ! -f .env ] || ! grep -q "IGNITER_INSTALLED=true" .env; then
log "Running TastyIgniter installer..."
php artisan igniter:install --no-interaction
# Mark as installed in .env
echo "IGNITER_INSTALLED=true" >> .env
log "TastyIgniter installed successfully"
else
log "TastyIgniter already installed"
fi
# Run database migrations if needed
log "Running database migrations..."
php artisan migrate --force
# Clear and cache configuration
log "Clearing and caching configuration..."
php artisan config:clear
php artisan config:cache
php artisan route:clear
php artisan route:cache
php artisan view:clear
php artisan view:cache
# Set proper permissions again after all operations
log "Setting final permissions..."
chown -R www-data:www-data /var/www/html
chmod -R 755 /var/www/html
chmod -R 775 /var/www/html/storage
chmod -R 775 /var/www/html/bootstrap/cache
log "Entrypoint script completed successfully"
# Execute the main command
exec "$@"
-96
View File
@@ -1,96 +0,0 @@
server {
listen 80;
server_name ${APP_URL:-localhost};
root /var/www/html/public;
index index.php;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;
charset utf-8;
client_max_body_size 100M;
fastcgi_read_timeout 1800;
# Logs
access_log /var/log/nginx/access.log combined buffer=512k flush=1m;
error_log /var/log/nginx/error.log warn;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_read_timeout 1800;
fastcgi_send_timeout 1800;
fastcgi_connect_timeout 1800;
}
# Deny access to . files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Allow fpm ping and status
location ~ ^/(fpm-status|fpm-ping)$ {
access_log off;
allow 127.0.0.1;
deny all;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_pass app:9000;
}
# Deny access to sensitive files
location ~* \.(env|log|git|key|md|yml|yaml|conf|ini|lock|json|sql)$ {
deny all;
}
# Assets caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires max;
log_not_found off;
access_log off;
add_header Cache-Control "public, no-transform";
try_files $uri =404;
}
# Deny access to hidden files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Deny access to composer files
location ~ composer\.(json|lock)$ {
deny all;
}
# Deny access to storage files
location ~ /storage/.*\.(php|php5|sh|pl|py|jsp|asp|htm|html|shtml|js)$ {
deny all;
}
}
-31
View File
@@ -1,31 +0,0 @@
[PHP]
; Production settings
display_errors = Off
display_startup_errors = Off
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED
log_errors = On
error_log = /var/log/php/error.log
memory_limit = 256M
max_execution_time = 60
max_input_time = 60
post_max_size = 100M
upload_max_filesize = 100M
max_input_vars = 3000
; Session settings
session.gc_maxlifetime = 1440
session.gc_probability = 1
session.gc_divisor = 100
; OpCache settings
opcache.enable = 1
opcache.enable_cli = 0
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 8
opcache.max_accelerated_files = 4000
opcache.revalidate_freq = 60
opcache.fast_shutdown = 1
opcache.enable_file_override = 0
; Date settings
date.timezone = UTC
-21
View File
@@ -1,21 +0,0 @@
[www]
user = www-data
group = www-data
listen = 9000
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500
php_admin_value[error_log] = /var/log/php/error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 256M
php_admin_value[max_execution_time] = 60
php_admin_value[max_input_time] = 60
php_admin_value[post_max_size] = 100M
php_admin_value[upload_max_filesize] = 100M
php_admin_value[max_input_vars] = 3000
+6
View File
@@ -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=
+20 -2
View File
@@ -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.