Skip to content

feat(self-host): align Docker Compose with Helm and overhaul self-hosting docs - #6225

Merged
waleedlatif1 merged 7 commits into
stagingfrom
selfhost-docs-gaps
Aug 3, 2026
Merged

feat(self-host): align Docker Compose with Helm and overhaul self-hosting docs#6225
waleedlatif1 merged 7 commits into
stagingfrom
selfhost-docs-gaps

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • Added a cron service to Docker Compose running the same 18 background jobs the Helm chart schedules as CronJobs. Scheduled workflows, every polling trigger, connector syncs, the outbox, and data drains previously never ran on Compose — silently, with no error
  • Bundled Redis in the Helm chart (redis.enabled, chart 1.4.0 → 1.5.0) to match Compose. An existing app.env.REDIS_URL, existingSecret, or External Secrets config takes precedence and suppresses the bundled instance, so upgrading a release that points at a managed Redis renders unchanged
  • Removed hosted free-tier plan caps from app.envDefaults. With billing disabled these are opt-in — presetting them enforced hosted limits on self-hosters and diverged from Compose, which presets nothing
  • Pinned Compose images behind SIM_VERSION and made missing secrets fail loudly instead of booting with empty values. CRON_SECRET degrades gracefully (cron exits with instructions, rest of the stack runs) so deployments predating the scheduler still start
  • Added scripts/check-cron-parity.ts to CI so docker/crontab and the Helm CronJobs can't drift apart
  • Rewrote the self-hosting docs: 14 new pages (architecture, integrations OAuth, background jobs, Redis, backup/restore, upgrades, security, networking, observability, scaling, verify, air-gapped, authentication, email) and 8 updated, reorganized into Install → Configure → Operate

The docs work started as a gap analysis against a hand-written customer runbook. Most of the gaps turned out to be product inconsistencies rather than missing docs, which is what the rest of this PR fixes.

Type of Change

  • New feature (non-breaking change which adds functionality)

Testing

Tested manually. Brought the full Compose stack up end to end — all six services healthy, 11/11 minute-cadence jobs succeeded against the live app, zero failures. Verified CRON_SECRET returns 401 when wrong or absent, and that an existing .env without it still starts with the scheduler self-disabling.

Helm: 98 unit tests (5 new for Redis modes), helm lint, kubeconform strict on default and all-components, all 10 example values files render. Verified upgrade paths render unchanged for managed Redis, existingSecret, External Secrets, and explicitly set plan limits.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Note for reviewers: ghcr.io/simstudioai/cron does not exist until this lands on main and CI publishes it. The compose file has a build: fallback so up -d works from a clone in the meantime, but docker compose pull will report it missing until then.

docker-compose.ollama.yml was deliberately left untouched — it has no cron service and no fail-fast secrets, so it's now inconsistent with docker-compose.prod.yml. Worth a follow-up.

…ting docs

Docker Compose shipped no scheduler, so scheduled workflows, every polling
trigger, connector syncs, the outbox, and data drains silently never ran.
Adds a cron service running the same 18 jobs the Helm chart schedules as
CronJobs, and closes the remaining behavioral gaps between the two paths:
bundled Redis in the chart, no hosted plan caps in chart defaults, pinned
image tags, and fail-fast secrets. A CI check keeps the schedulers in sync.

Also rewrites the self-hosting docs: 14 new pages, 8 updated, reorganized
into Install / Configure / Operate.
@waleedlatif1
waleedlatif1 requested a review from a team as a code owner August 3, 2026 20:50
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview Aug 3, 2026 9:45pm

Request Review

@cursor

cursor Bot commented Aug 3, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes affect production deployment paths (Compose startup requirements, Helm defaults, background job scheduling) and image publishing; misconfiguration could stop schedules or alter limit behavior on upgrade, but core app auth paths are documentation and operational alignment rather than application logic rewrites.

Overview
Docker Compose now runs the same 18 background jobs as Kubernetes via a new cron service (supercronic + docker/crontab), authenticated with CRON_SECRET. SIM_VERSION pins app/realtime/migrations together; required secrets fail at startup instead of booting empty. CRON_SECRET missing only stops cron (graceful upgrade path).

Helm 1.5.0 bundles Redis by default (existing REDIS_URL still wins). Hosted free-tier limit presets are removed from app.envDefaults so self-hosted installs match unlimited Compose behavior unless you opt in.

CI builds/publishes ghcr.io/simstudioai/cron, skips ECR for cron, and runs scripts/check-cron-parity.ts so docker/crontab and Helm cronjobs cannot drift.

Docs & onboarding add many self-hosting guides (architecture, auth, email, Redis, networking, security, scaling, upgrades, verify, integrations OAuth, etc.), refresh Docker/Kubernetes/env-var pages, and document generating secrets including CRON_SECRET in README/CONTRIBUTING.

Reviewed by Cursor Bugbot for commit a95c90f. Configure here.

Comment thread apps/docs/content/docs/en/platform/self-hosting/docker.mdx
@greptile-apps

greptile-apps Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR aligns Docker Compose background scheduling and Redis behavior with Helm while substantially expanding self-hosting operational documentation.

  • Adds the Compose cron service and scheduler-parity CI check.
  • Adds bundled Helm Redis with Secret-aware environment precedence.
  • Introduces fail-fast Compose secret handling and version-pinned images.
  • Expands installation, security, networking, backup, scaling, and operations guidance.

Confidence Score: 4/5

The PR should not merge until the existing NetworkPolicy failure for Secret-selected external Redis is resolved or made safe by default.

The current app and realtime policies permit Redis traffic only to the bundled pod selector and otherwise allow external traffic on port 443, so an external Redis selected through a Secret remains unreachable on standard Redis ports unless operators independently add a custom egress rule; the prior reply documents this residual requirement rather than removing it.

Files Needing Attention: helm/sim/templates/networkpolicy.yaml

Important Files Changed

Filename Overview
helm/sim/templates/_helpers.tpl Revises bundled Redis selection so secret-manager modes retain a safe fallback while direct app.env.REDIS_URL values suppress it.
helm/sim/templates/configmap-redis.yaml Provides the bundled Redis URL through an earlier envFrom source so operator Secrets can override it.
helm/sim/templates/networkpolicy.yaml Adds bundled Redis egress rules, while external Redis selected through a Secret still requires a manually configured egress rule.
helm/sim/values.schema.json Adds Redis configuration schema, but its secret-manager suppression description disagrees with the current helper behavior.
docker-compose.prod.yml Adds Redis and cron services, image version controls, and required-secret validation for the Compose deployment.
docker/crontab Defines the Compose schedules corresponding to Helm CronJobs.
scripts/check-cron-parity.ts Adds CI validation that Compose and Helm scheduler endpoints and schedules remain aligned.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Scheduler[Compose cron / Helm CronJobs] -->|Bearer CRON_SECRET| App[Sim app]
  App --> Postgres[(PostgreSQL)]
  App --> RedisChoice{REDIS_URL source}
  RedisChoice -->|Bundled ConfigMap| Bundled[(Bundled Redis)]
  RedisChoice -->|Later Secret override| External[(External Redis)]
  Realtime[Realtime] --> RedisChoice
Loading

Reviews (6): Last reviewed commit: "feat(helm): add networkPolicy.allowExter..." | Re-trigger Greptile

Comment thread helm/sim/templates/_helpers.tpl Outdated
… backup docs

The scheduler-parity check pulled a full dependency install into the
chart-validation job, which fails building isolated-vm on that runner.
Rewritten to use only node builtins so the job installs nothing.

Also removes the air-gapped and backup/restore pages, and stops pinning a
concrete release in the docs so the examples do not go stale each release.
…plied

Suppressing Redis whenever a secret mode was active left those deployments
with no Redis at all — REDIS_URL is optional there and both shipped examples
omit it. The chart now steps aside only on a detectable signal: an explicit
app.env.REDIS_URL, an ESO remoteRefs.app.REDIS_URL mapping, or the new
redis.provideUrl=false opt-out for a pre-created Secret it cannot read.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

realtime read BETTER_AUTH_URL directly and fell back to localhost while
simstudio derived it from NEXT_PUBLIC_APP_URL, so setting only the public
origin left realtime authenticating against http://localhost:3000.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 58bd1ba. Configure here.

Comment thread helm/sim/templates/_helpers.tpl Outdated
…ue always wins

Injecting REDIS_URL as an inline container env made it beat every envFrom
source, so a REDIS_URL held in a pre-created Secret or synced by External
Secrets was silently shadowed and traffic moved to a fresh in-cluster Redis.

Kubernetes resolves duplicate envFrom keys by letting the last source win, so
the bundled URL now ships as a ConfigMap listed before the app Secret. Any
operator-supplied value overrides it without the chart needing to read it,
which also removes the redis.provideUrl flag the previous attempt required.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit d2f7e2c. Configure here.

Comment thread helm/sim/templates/networkpolicy.yaml
The default NetworkPolicy allows 443 plus the bundled Postgres and Redis by
pod selector. Anything you run outside the chart on another port needs its own
rule, which is easiest to miss when REDIS_URL arrives via a Secret the chart
cannot inspect. Adds a copyable example to the production checklist and the
security guide.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit ee2d004. Configure here.

The default policy allows 443 plus the bundled Postgres and Redis by pod
selector, so a managed datastore on another port needs a hand-written CIDR
rule — awkward when REDIS_URL arrives via a Secret the chart cannot inspect.

Adds an opt-in switch that drops the port restriction while still blocking the
cloud metadata endpoints. Defaults to false, keeping this chart stricter than
the common chart default of unrestricted egress.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

Added networkPolicy.allowExternalEgress to close out the egress point properly rather than leaving it to documentation.

The default policy allows 443 plus the bundled Postgres and Redis by pod selector, so any datastore running outside the chart on another port needs a hand-written CIDR rule. That is awkward precisely in the case raised — a REDIS_URL arriving via a Secret the chart cannot inspect, so it cannot generate the rule.

networkPolicy:
  enabled: true
  allowExternalEgress: true   # drops the port restriction

Cloud metadata endpoints (169.254.169.254/32, 169.254.170.2/32) stay blocked either way — that exclusion is not negotiable and is asserted in the tests.

Defaults to false, so nothing changes for existing installs. Worth noting the comparison: Bitnami's charts ship allowExternalEgress: true, i.e. unrestricted egress out of the box, and are criticised for it. This chart stays on the stricter side and makes the relaxation explicit and opt-in.

Verified: 443-only by default, unrestricted when enabled, metadata blocked in both; kubeconform clean in both modes; 97 chart tests.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit a95c90f. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

On the remaining 4/5 — I want to push back on the "safe by default" framing with the actual defaults, because I think it rests on a factual error.

networkPolicy.enabled defaults to false. A default install renders zero NetworkPolicy objects:

$ helm template sim ./helm/sim --values helm/sim/ci/default-values.yaml | grep -c 'kind: NetworkPolicy'
0

So no default install is affected by this. Reaching the scenario requires an operator to deliberately set networkPolicy.enabled: true — at which point enumerating egress is the explicit purpose of the feature they turned on, and the chart's production checklist already directs them to review it.

Two further points:

  1. This is not Redis-specific, and not new. An external Postgres behaves identically and always has — with externalDatabase.enabled=true, the rendered app policy contains zero rules for 5432, because the chart cannot know the host. Redis now matches that long-standing behavior rather than diverging from it.

  2. Auto-generating the rule is not possible in the case being described. The premise is a REDIS_URL held in a Secret, which the chart cannot read at render time — there is no host to turn into an ipBlock. The only alternative that would satisfy "safe by default" is permitting unrestricted egress by default, which is what Bitnami does and is actively criticised as a security regression. Weakening egress for every install to serve an opt-in configuration is the wrong trade.

What this PR does provide, for an operator who has opted into NetworkPolicy and runs a managed datastore: a copyable CIDR rule in both the production checklist and the security guide, and networkPolicy.allowExternalEgress: true as a one-line relaxation that still blocks the metadata endpoints.

I am treating this as a deliberate posture rather than an unresolved defect, and leaving the default strict. Happy to revisit if a maintainer prefers the permissive default.

@waleedlatif1
waleedlatif1 merged commit 3de63c9 into staging Aug 3, 2026
33 checks passed
@waleedlatif1
waleedlatif1 deleted the selfhost-docs-gaps branch August 3, 2026 21:52
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.

1 participant