Skip to content

feat(template): add OpenAnalytics - #1529

Draft
teo-goulois wants to merge 2 commits into
easypanel-io:mainfrom
teo-goulois:agent/add-openanalytics-template
Draft

feat(template): add OpenAnalytics#1529
teo-goulois wants to merge 2 commits into
easypanel-io:mainfrom
teo-goulois:agent/add-openanalytics-template

Conversation

@teo-goulois

Copy link
Copy Markdown

What changed

  • add a native EasyPanel template for OpenAnalytics v0.4.2
  • provision PostgreSQL, ClickHouse, two isolated Valkey instances, initialization jobs, application services, and a Caddy edge as separate EasyPanel resources
  • generate per-install database, queue, identity, signing, and credential-encryption secrets
  • configure four public domains from one base domain: app, API, collector, and realtime
  • include the official OpenAnalytics logo, a populated product screenshot, metadata, links, deployment instructions, and resource/architecture requirements

Why

OpenAnalytics provides an official self-hosted stack and one-click platform wiring, but it is not currently available in EasyPanel's template catalogue. This template adapts the official v0.4.2 service boundaries to native EasyPanel resources so users can deploy the complete stack without maintaining a Compose checkout.

User impact

Users can deploy OpenAnalytics from a single template form while retaining separate services, persistent volumes, generated secrets, and understandable domain routing. The template does not contain installation-specific domains or credentials.

Validation

  • npm run build-templates
  • OpenAnalytics-only TypeScript check with tsc --noEmit
  • Prettier check for index.ts and meta.yaml
  • generated output validated with EasyPanel's templateSchema
  • verified 14 unique native services, no Compose service, and four edge domains
  • checked that templates/list.json only adds the openanalytics entry
  • verified the logo matches the official OpenAnalytics asset

The full npm run build reached Next.js's catalogue-wide Checking validity of types... phase but did not complete locally after several minutes; it was stopped. The template generation and targeted type checks above completed successfully.

@netlify

netlify Bot commented Aug 16, 2026

Copy link
Copy Markdown

Deploy Preview for easypanel-templates ready!

Name Link
🔨 Latest commit 2538fe2
🔍 Latest deploy log https://app.netlify.com/projects/easypanel-templates/deploys/6a82a8527c33690008007a33
😎 Deploy Preview https://deploy-preview-1529--easypanel-templates.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@Uaghazade1

Copy link
Copy Markdown

Hi @teo-goulois, I'm one of the OpenAnalytics maintainers. Thank you for this,
it is a remarkably careful adaptation. The service boundaries, the env names
(including the CLICKHOUSE_DATABASE vs CLICKHOUSE_DB split between the
migration CLI and the running services), the idempotent keygen with the
per-service key split, and the GeoIP bootstrap all match our one-click
reference (infra/selfhost/docker-compose.coolify.yml) exactly. Before this
merges, though, one blocking issue and a few smaller ones.

Blocking: named volumes are per-service on Easypanel, so the shared mounts
never actually share.

Six volumes are mounted by name in two or more services: keys-api,
keys-gateway, keys-realtime, keys-collector (keygen plus the consumer of
each), keyring (keygen, api, worker) and geoip (the geoip job and the
collector). On Easypanel each service gets its own volume namespace, so every
consumer mounts a fresh empty volume rather than the one keygen and the geoip
job wrote into. Other templates in this catalogue work around exactly this:
penpot and twenty share data by bind-mounting the owning service's volume
directory, e.g.

{
  type: "bind",
  hostPath: `/etc/easypanel/projects/$(PROJECT_NAME)/${input.serviceName}-keygen/volumes/keys-api`,
  mountPath: "/keys",
}

As written, the failure is total rather than partial: our services
deliberately refuse to start when a configured key file path names no file
(that is the least-privilege design working, not a bug), so api, gateway,
realtime and collector will crash-loop forever and the stack never comes up.
The fix is mechanical: keep type: "volume" on the writers (keygen and the
geoip job) and switch every consumer mount to a bind on the writer's path.
Please keep the four separate key directories when you do; the split is
deliberate. Each verifier must only ever see its own public half, never a
private one, so one shared /keys bind for everybody would undo it.

Worth verifying: what Easypanel does with the three one-shots after they
exit.

keygen, geoip and migrate are jobs that exit 0. They are idempotent by
design, so restarts are safe, but if Easypanel restarts exited app services
they will loop forever: keygen re-runs apk add openssl (a network fetch) on
every cycle, migrate reconnects to Postgres and ClickHouse continuously, and
the panel shows all three as permanently restarting. If the platform has a
pattern for one-shot jobs, it is worth applying; appending a long sleep after
success is the crude version.

Minor:

  • The Website link in meta.yaml points to openanalytics.io, which
    redirects to openanalytics.eu, an unrelated Belgian data-science company.
    Our site is https://getopen.so.
  • Our compose sets a nofile ulimit of 262144 on ClickHouse; if Easypanel can
    express ulimits it is worth carrying over, otherwise installs may hit "too
    many open files" under load.
  • Where the mount schema allows it, the key and keyring mounts on the
    consumer side should be read-only.

Everything else I checked is right: the nine images pinned to v0.4.2 (the
v0.4.0 floor for the collector serving oa.js is respected), the Caddy edge
reinstating our identity-header hygiene with the exact header list, the
first-run account claim via AUTH_PASSWORD_SIGNIN, the amd64/RAM/disk
numbers, and the DB-IP attribution.

Since none of the static checks can catch the volume behaviour, could you run
one live deploy on a real Easypanel instance after the fix and confirm the
first-run screen loads and a pageview arrives? Happy to help verify; feel free
to ping me here or open an issue on our repo.

@teo-goulois

Copy link
Copy Markdown
Author

@Uaghazade1 Thanks for the careful review — fixed in 2538fe2.

  • Kept the named volumes only on the keygen and geoip writer services.
  • Switched each consumer to a bind mount of the owning EasyPanel volume path.
  • Preserved all four key directories and their per-service exposure.
  • Updated the website URL to https://getopen.so.

I also tested the updated template with a clean deployment on a real EasyPanel instance:

  • all long-running services became healthy;
  • keygen and geoip exited and stayed stopped; all their consumers started successfully, confirming that the generated keys, keyring and GeoIP files were available;
  • migrate initially started before the PostgreSQL service name was resolvable, EasyPanel retried it 10 seconds later, and it then applied 45 PostgreSQL migrations and 21 ClickHouse migrations with 0 pending; it stayed stopped afterward, with no success loop;
  • the first-run account/site flow completed successfully;
  • the app, API health endpoint, realtime health endpoint and /oa.js all returned HTTP 200;
  • a tracker-generated pageview was received successfully.

The current EasyPanel template schema does not expose read-only bind mounts or ulimits, so I did not add unsupported fields.

Thanks again — the volume ownership issue was exactly the missing EasyPanel platform detail.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants