-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrender.yaml
More file actions
236 lines (227 loc) · 9.85 KB
/
Copy pathrender.yaml
File metadata and controls
236 lines (227 loc) · 9.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# Roomote Render Blueprint definition.
#
# This file is the maintained source of truth for deploying Roomote on
# Render (https://render.com) as a Blueprint. Unlike the Railway template
# (deploy/railway/template.yaml), Render consumes this file directly: the
# "Deploy to Render" button and Render's Blueprint sync both read
# `render.yaml` from the repository root, which is why this file does not
# live under deploy/render/. The operator guide is deploy/render/README.md.
#
# Design notes:
# - One shared `roomote-app` image runs web, api, controller, and bullmq;
# each service selects its process through the image's entrypoint
# dispatcher (`.docker/app/entrypoint.sh`). Render's `dockerCommand`
# bypasses the image entrypoint, so every app service uses the full
# `/roomote/.docker/app/entrypoint.sh <service>` form. Keep these
# commands quote-free: Render's `dockerCommand` parser passes quote
# characters through literally instead of stripping them, so a
# `/bin/sh -c '...'` wrapper reaches the shell as one quoted word and
# exits 127.
# - Render Blueprints cannot interpolate service references into strings
# (there is no `https://${{...}}` composition like Railway), so each app
# service receives host-only references (`ROOMOTE_WEB_HOST`,
# `ROOMOTE_API_HOST`, `ROOMOTE_MINIO_HOST`, `ROOMOTE_MINIO_HOSTPORT`) and
# the entrypoint dispatcher composes the URL-shaped variables
# (`R_APP_URL`, `TRPC_URL`, `S3_ENDPOINT`, `S3_PRESIGN_ENDPOINT`)
# from them when they are unset. The cross-service references use
# Render's `fromService` + `envVarKey` form against the Render-provided
# `RENDER_EXTERNAL_HOSTNAME`; the first-boot verification in
# deploy/render/README.md ("Maintaining the template") must confirm the
# app services receive real hostnames, not empty strings.
# - Shared values live in the `roomote-shared` environment group. Render
# generates each `generateValue: true` secret once per Blueprint deploy
# and every service referencing the group sees the same value, so
# `ENCRYPTION_KEY` is identical everywhere (unlike Railway's
# per-occurrence `secret()` function). The MinIO password is the one
# exception: environment groups cannot reference services, so MinIO owns
# the generated `MINIO_ROOT_PASSWORD` and the app services mirror it into
# `S3_SECRET_ACCESS_KEY` with a `fromService` reference.
# - Render has no Docker socket, so task execution must use a hosted
# sandbox provider (modal by default; e2b and daytona also work).
# EXCLUDED_COMPUTE_PROVIDERS=docker hides the unusable provider.
# - Zero deploy-time inputs. Modal tokens and model provider keys are
# entered in the /setup wizard after first boot and stored encrypted in
# Postgres. Auth keypairs come from R_AUTO_GENERATE_KEYS=true.
# - The api service must be publicly reachable: hosted-sandbox workers and
# GitHub webhooks call `TRPC_URL` directly. MinIO is public too because
# presigned artifact URLs (`S3_PRESIGN_ENDPOINT`) must be reachable from
# workers and browsers; Render routes its HTTPS domain to port 9000.
# - The Blueprint tracks the mutable `:main` image alias (stable main
# branch builds) and contains no version strings. The images bake
# RELEASE_VERSION; the app derives DOCKER_WORKER_IMAGE and
# MODAL_BASE_IMAGE_REF from it, and the controller reads the worker
# release version from the VERSION file inside worker-current.tar.gz.
# Production deployments can pin an immutable `v*` or `main-<sha>` tag
# in the four `image.url` fields instead; nothing else needs editing.
# Render does not redeploy when a mutable alias moves — redeploy the
# app services to pick up new builds.
# - SETUP_TOKEN is generated by default because onrender.com domains are
# publicly reachable. Copy it from the roomote-shared environment group
# in the Render dashboard to open /setup.
# - Live previews stay disabled by default. They need a customer-owned
# wildcard custom domain; see "Live previews" in deploy/render/README.md.
# - Both published images are public on GHCR, and MinIO comes from Docker
# Hub, so no registry credentials are needed.
previews:
generation: 'off'
services:
# Render Key Value (Redis-compatible). BullMQ needs `noeviction`; the
# empty ipAllowList keeps it private-network only.
- type: keyvalue
name: roomote-redis
plan: starter
ipAllowList: []
maxmemoryPolicy: noeviction
# Bundled S3-compatible artifact storage. Public: presigned artifact URLs
# use this service's HTTPS domain, routed to the S3 API on port 9000 (the
# console on 9001 stays private). Replacing MinIO with external S3/R2 is
# a config-only swap; see deploy/render/README.md.
- type: web
name: roomote-minio
runtime: image
image:
# Pinned to an immutable MinIO release: this service owns a persistent
# disk, so a mutable `latest` tag could pull an unreviewed breaking
# change on redeploy. Bump deliberately alongside a disk backup.
url: docker.io/minio/minio:RELEASE.2025-09-07T16-13-09Z@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
plan: starter
dockerCommand: minio server /data --address :9000 --console-address :9001
healthCheckPath: /minio/health/live
disk:
name: minio-data
mountPath: /data
sizeGB: 10
envVars:
# Tells Render's proxy which port serves HTTP (the S3 API).
- key: PORT
value: 9000
# Must stay equal to S3_ACCESS_KEY_ID in the roomote-shared group.
- key: MINIO_ROOT_USER
value: roomote
# The app services mirror this generated value into
# S3_SECRET_ACCESS_KEY via fromService references.
- key: MINIO_ROOT_PASSWORD
generateValue: true
- type: web
name: roomote-api
runtime: image
image:
url: ghcr.io/roocodeinc/roomote-app:main # or pin an immutable v* / main-<sha> tag
plan: starter # upgrade instance types from the dashboard as usage grows
healthCheckPath: /health/liveness
# Runs schema migrations before each new deploy starts serving. On a
# fresh Blueprint the other app services boot in parallel with this
# first migration pass and wait for it with bounded backoff instead of
# crash-looping.
preDeployCommand: /roomote/.docker/app/entrypoint.sh db-migrate
dockerCommand: /roomote/.docker/app/entrypoint.sh api
envVars: &roomote-app-env
- fromGroup: roomote-shared
- key: DATABASE_URL
fromDatabase:
name: roomote-postgres
property: connectionString
- key: REDIS_URL
fromService:
type: keyvalue
name: roomote-redis
property: connectionString
- key: S3_SECRET_ACCESS_KEY
fromService:
type: web
name: roomote-minio
envVarKey: MINIO_ROOT_PASSWORD
# Host-only references composed into URLs by the dockerCommand (see
# the design notes at the top of this file).
- key: ROOMOTE_WEB_HOST
fromService:
type: web
name: roomote-web
envVarKey: RENDER_EXTERNAL_HOSTNAME
- key: ROOMOTE_API_HOST
fromService:
type: web
name: roomote-api
envVarKey: RENDER_EXTERNAL_HOSTNAME
- key: ROOMOTE_MINIO_HOST
fromService:
type: web
name: roomote-minio
envVarKey: RENDER_EXTERNAL_HOSTNAME
- key: ROOMOTE_MINIO_HOSTPORT
fromService:
type: web
name: roomote-minio
property: hostport
# This is the app origin users open in the browser.
- type: web
name: roomote-web
runtime: image
image:
url: ghcr.io/roocodeinc/roomote-app:main
plan: starter
healthCheckPath: /health
dockerCommand: /roomote/.docker/app/entrypoint.sh web
envVars: *roomote-app-env
- type: worker
name: roomote-controller
runtime: image
image:
url: ghcr.io/roocodeinc/roomote-app:main
plan: starter
# The controller may be inside a hosted-sandbox worker spawn when a
# deploy replaces the container; 300 is Render's maximum grace period.
maxShutdownDelaySeconds: 300
dockerCommand: /roomote/.docker/app/entrypoint.sh controller
envVars: *roomote-app-env
- type: worker
name: roomote-bullmq
runtime: image
image:
url: ghcr.io/roocodeinc/roomote-app:main
plan: starter
dockerCommand: /roomote/.docker/app/entrypoint.sh bullmq
envVars: *roomote-app-env
databases:
- name: roomote-postgres
plan: basic-1gb # the smallest instance type comfortable for the control plane
postgresMajorVersion: '17'
databaseName: roomote
user: roomote
envVarGroups:
# Shared by every app service. Render generates each secret once and all
# services referencing the group see the same value.
- name: roomote-shared
envVars:
- key: R_APP_ENV
value: production
- key: ROOMOTE_DOCKER_LOAD_ENV_FILE
value: 'false'
- key: R_AUTO_GENERATE_KEYS
value: 'true'
- key: ENCRYPTION_KEY
generateValue: true
- key: ARTIFACT_SIGNING_KEY
generateValue: true
- key: DASHBOARD_PASSWORD
generateValue: true
# onrender.com domains are publicly reachable, so gate /setup by
# default. Copy the value from this group in the Render dashboard.
- key: SETUP_TOKEN
generateValue: true
- key: S3_ACCESS_KEY_ID
value: roomote
- key: S3_REGION
value: us-east-1
- key: S3_BUCKET_ARTIFACTS
value: roomote-artifacts
- key: S3_AUTO_CREATE_BUCKET
value: 'true' # api creates the MinIO bucket at boot; no manual mc step
- key: DEFAULT_COMPUTE_PROVIDER
value: modal
- key: EXCLUDED_COMPUTE_PROVIDERS
value: docker
# Only the controller reads this, but the value is a constant baked
# into the app image, so the shared group is the simplest home for it.
- key: DOCKER_WORKER_RELEASE_PATH
value: /roomote/releases/worker-current.tar.gz