dash 2.12.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/kamal/cli/app/boot.rb +47 -11
- data/lib/kamal/cli/app/rollout_boot.rb +59 -0
- data/lib/kamal/cli/app/ssl_certificates.rb +12 -3
- data/lib/kamal/cli/app.rb +74 -7
- data/lib/kamal/cli/base.rb +41 -32
- data/lib/kamal/cli/doctor/config_checks.rb +28 -0
- data/lib/kamal/cli/doctor/endpoint_checks.rb +114 -0
- data/lib/kamal/cli/doctor/host_checks.rb +178 -0
- data/lib/kamal/cli/doctor.rb +112 -0
- data/lib/kamal/cli/healthcheck/drift_error.rb +7 -0
- data/lib/kamal/cli/healthcheck/poller.rb +60 -10
- data/lib/kamal/cli/main.rb +63 -0
- data/lib/kamal/cli/proxy/drift.rb +39 -0
- data/lib/kamal/cli/proxy/loadbalancer_claim.rb +70 -0
- data/lib/kamal/cli/proxy/loadbalancer_reboot.rb +87 -0
- data/lib/kamal/cli/proxy/reboot.rb +173 -0
- data/lib/kamal/cli/proxy.rb +201 -34
- data/lib/kamal/cli/prune.rb +7 -4
- data/lib/kamal/cli/templates/sample_hooks/post-app-stop.sample +9 -0
- data/lib/kamal/cli/templates/sample_hooks/post-proxy-deploy.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-app-stop.sample +12 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-proxy-deploy.sample +3 -0
- data/lib/kamal/cli.rb +1 -0
- data/lib/kamal/commands/app/proxy.rb +12 -0
- data/lib/kamal/commands/app.rb +8 -0
- data/lib/kamal/commands/base.rb +11 -1
- data/lib/kamal/commands/docker.rb +5 -0
- data/lib/kamal/commands/loadbalancer.rb +76 -34
- data/lib/kamal/commands/proxy.rb +111 -11
- data/lib/kamal/commands/prune.rb +16 -2
- data/lib/kamal/commands/server.rb +5 -0
- data/lib/kamal/configuration/accessory.rb +9 -8
- data/lib/kamal/configuration/boot.rb +38 -7
- data/lib/kamal/configuration/docs/accessory.yml +28 -1
- data/lib/kamal/configuration/docs/boot.yml +17 -2
- data/lib/kamal/configuration/docs/configuration.yml +2 -1
- data/lib/kamal/configuration/docs/proxy.yml +844 -24
- data/lib/kamal/configuration/docs/role.yml +86 -0
- data/lib/kamal/configuration/loadbalancer.rb +81 -6
- data/lib/kamal/configuration/proxy/acme.rb +69 -0
- data/lib/kamal/configuration/proxy/run.rb +194 -5
- data/lib/kamal/configuration/proxy.rb +495 -18
- data/lib/kamal/configuration/role/healthcheck.rb +95 -0
- data/lib/kamal/configuration/role.rb +104 -1
- data/lib/kamal/configuration/validator/proxy.rb +623 -10
- data/lib/kamal/configuration/validator/role.rb +26 -0
- data/lib/kamal/configuration/validator.rb +26 -3
- data/lib/kamal/configuration.rb +197 -0
- data/lib/kamal/sshkit_with_ext.rb +27 -2
- data/lib/kamal/utils.rb +22 -2
- data/lib/kamal/version.rb +1 -1
- data/lib/kamal.rb +11 -0
- metadata +18 -2
|
@@ -12,6 +12,36 @@
|
|
|
12
12
|
#
|
|
13
13
|
proxy:
|
|
14
14
|
|
|
15
|
+
|
|
16
|
+
# Where each option lives when a loadbalancer fronts the fleet
|
|
17
|
+
#
|
|
18
|
+
# Every deploy option has exactly one home (see
|
|
19
|
+
# Kamal::Configuration::Proxy::DEPLOY_OPTION_DISPOSITIONS - the layering
|
|
20
|
+
# contract that enforces this):
|
|
21
|
+
#
|
|
22
|
+
# edge - applied only by the loadbalancer, stripped from the per-host
|
|
23
|
+
# proxies: host/hosts, ssl (certificates, on-demand, mTLS),
|
|
24
|
+
# ssl_redirect, ssl_staging, ssl_domains, basic_auth,
|
|
25
|
+
# allow_ips, client_ip, rate_limit, session_affinity,
|
|
26
|
+
# canonical_host, redirects, cache, read_routing
|
|
27
|
+
#
|
|
28
|
+
# per-app - applied only by the per-host proxies, next to the app:
|
|
29
|
+
# headers, rewrites, intercept_errors, sleep, compress
|
|
30
|
+
#
|
|
31
|
+
# both - each layer runs its own copy, deliberately: healthcheck,
|
|
32
|
+
# response/request timeouts, path timeouts, target pool
|
|
33
|
+
# tuning, buffering, path_prefix, strip_path_prefix,
|
|
34
|
+
# forward_headers, logging, exclude_metrics_paths
|
|
35
|
+
#
|
|
36
|
+
# Without a loadbalancer the single proxy is every layer at once and the
|
|
37
|
+
# whole surface applies to it. `loadbalancer`, `reboot_on_deploy` and `run`
|
|
38
|
+
# configure the containers themselves rather than a deployment, so they
|
|
39
|
+
# have no layer.
|
|
40
|
+
|
|
41
|
+
# ==========================================================================
|
|
42
|
+
# 1. Essentials — enough for most apps
|
|
43
|
+
# ==========================================================================
|
|
44
|
+
|
|
15
45
|
# Hosts
|
|
16
46
|
#
|
|
17
47
|
# The hosts that will be used to serve the app. The proxy will only route requests
|
|
@@ -22,17 +52,71 @@ proxy:
|
|
|
22
52
|
#
|
|
23
53
|
# Specify one of `host` or `hosts`.
|
|
24
54
|
host: foo.example.com
|
|
55
|
+
|
|
25
56
|
hosts:
|
|
26
57
|
- foo.example.com
|
|
27
58
|
- bar.example.com
|
|
28
|
-
|
|
59
|
+
|
|
29
60
|
# Loadbalancer
|
|
30
61
|
#
|
|
31
62
|
# Specify a host to run the loadbalancer on. The loadbalancer will distribute requests
|
|
32
63
|
# to all web hosts. If not specified but multiple web hosts are configured, the first
|
|
33
64
|
# web host will be used as the loadbalancer host.
|
|
65
|
+
#
|
|
66
|
+
# The host may be a dedicated machine outside of `servers:`, or one of the web hosts —
|
|
67
|
+
# in which case the loadbalancer takes over that host's proxy container.
|
|
68
|
+
#
|
|
69
|
+
# Set `true` to always run the loadbalancer on the first host of the primary role, even
|
|
70
|
+
# when that role has a single host, or `false` to opt out of the automatic activation
|
|
71
|
+
# for multi-host primary roles.
|
|
34
72
|
loadbalancer: lb.example.com
|
|
35
73
|
|
|
74
|
+
# Sharing one loadbalancer between several kamal apps
|
|
75
|
+
#
|
|
76
|
+
# More than one kamal app may point `loadbalancer:` at the same host. Each app
|
|
77
|
+
# registers its own service on the shared kamal-proxy and keeps its own
|
|
78
|
+
# deploy.yml; the load balancer multiplexes them by hostname.
|
|
79
|
+
#
|
|
80
|
+
# The rules kamal enforces for that topology:
|
|
81
|
+
#
|
|
82
|
+
# * Service state survives a reboot. kamal-proxy persists its services in the
|
|
83
|
+
# `kamal-loadbalancer-config` volume (or `kamal-proxy-config` when the
|
|
84
|
+
# loadbalancer shares a proxy host), and `kamal proxy reboot` only replaces
|
|
85
|
+
# the container. Rebooting from app A does not drop app B's routes — the
|
|
86
|
+
# surviving service list is printed after the restart.
|
|
87
|
+
#
|
|
88
|
+
# * Service names must be unique across apps. The loadbalancer registers a
|
|
89
|
+
# service under the bare `service:` name, so two apps — or two destinations
|
|
90
|
+
# of one app — sharing a name would take over each other's routes. The first
|
|
91
|
+
# app to deploy claims the name in `.kamal/loadbalancer/services/<service>`
|
|
92
|
+
# on the loadbalancer host; a second app deploying the same name fails at
|
|
93
|
+
# deploy time instead of silently winning.
|
|
94
|
+
#
|
|
95
|
+
# * Apps sharing a loadbalancer must agree on `proxy/run`. All of them boot the
|
|
96
|
+
# same container, so the first app to boot records its run configuration in
|
|
97
|
+
# `.kamal/loadbalancer/run_config`. Another app booting with a different
|
|
98
|
+
# `proxy/run` fails; the same app changing its own gets the usual drift
|
|
99
|
+
# warning and applies it on `kamal proxy reboot`.
|
|
100
|
+
#
|
|
101
|
+
# * `kamal proxy remove` refuses while other apps are installed on the
|
|
102
|
+
# loadbalancer host, exactly as it does for a proxy host. Use `--force` to
|
|
103
|
+
# override, which removes the shared loadbalancer for every app on it.
|
|
104
|
+
|
|
105
|
+
# Automatic proxy reboot on deploy
|
|
106
|
+
#
|
|
107
|
+
# When `kamal deploy` detects that the running kamal-proxy container was
|
|
108
|
+
# started with a different image, version or run options than the current
|
|
109
|
+
# configuration, it reboots the proxy automatically — one host at a time —
|
|
110
|
+
# before booting the app.
|
|
111
|
+
#
|
|
112
|
+
# Set to false to opt out. Kamal will then print a warning when drift is
|
|
113
|
+
# detected and leave the proxy untouched until you run `kamal proxy reboot`.
|
|
114
|
+
#
|
|
115
|
+
# Root-level `proxy` setting only; ignored inside role-specific proxy blocks.
|
|
116
|
+
#
|
|
117
|
+
# Defaults to true:
|
|
118
|
+
reboot_on_deploy: false
|
|
119
|
+
|
|
36
120
|
# App port
|
|
37
121
|
#
|
|
38
122
|
# The port the application container is exposed on.
|
|
@@ -63,9 +147,29 @@ proxy:
|
|
|
63
147
|
#
|
|
64
148
|
# Kamal supports loading custom SSL certificates directly from secrets. You should
|
|
65
149
|
# pass a hash mapping the `certificate_pem` and `private_key_pem` to the secret names.
|
|
150
|
+
#
|
|
151
|
+
# The hash is also home to the rest of the TLS surface:
|
|
152
|
+
#
|
|
153
|
+
# `on_demand_url` turns on on-demand TLS: instead of a fixed `host` list, the
|
|
154
|
+
# proxy asks this endpoint whether it may issue a certificate for the hostname
|
|
155
|
+
# in an incoming handshake. A path is resolved against a healthy app target; an
|
|
156
|
+
# absolute http(s) URL is called directly. Answer 2xx to approve. On-demand TLS
|
|
157
|
+
# replaces the static hostname list, so it cannot be combined with
|
|
158
|
+
# `host`/`hosts`, with `certificate_pem`, or with `ssl_domains` — kamal rejects
|
|
159
|
+
# those at config time, because kamal-proxy would reject the deploy and there
|
|
160
|
+
# is no sensible winner to pick.
|
|
161
|
+
#
|
|
162
|
+
# `client_ca_pem` requires mutual TLS: clients must present a certificate
|
|
163
|
+
# signed by this CA bundle. Like `certificate_pem` it names a secret in
|
|
164
|
+
# `.kamal/secrets`; the content is uploaded next to the app's TLS material
|
|
165
|
+
# under `.kamal/proxy/apps-config`, and the proxy is given the path it sees
|
|
166
|
+
# inside its own container. An empty secret fails the deploy rather than
|
|
167
|
+
# silently turning mTLS off.
|
|
66
168
|
ssl:
|
|
67
169
|
certificate_pem: CERTIFICATE_PEM
|
|
68
170
|
private_key_pem: PRIVATE_KEY_PEM
|
|
171
|
+
on_demand_url: https://app.example.com/api/v1/tls/ask
|
|
172
|
+
client_ca_pem: CLIENT_CA_PEM
|
|
69
173
|
# ### Notes
|
|
70
174
|
# - If the certificate or key is missing or invalid, deployments will fail.
|
|
71
175
|
# - Always handle SSL certificates and private keys securely. Avoid hard-coding them in source control.
|
|
@@ -77,20 +181,37 @@ proxy:
|
|
|
77
181
|
# HTTPS traffic), you can disable this redirect by setting `ssl_redirect: false`:
|
|
78
182
|
ssl_redirect: false
|
|
79
183
|
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
# Whether to forward the `X-Forwarded-For` and `X-Forwarded-Proto` headers.
|
|
184
|
+
# SSL staging
|
|
83
185
|
#
|
|
84
|
-
#
|
|
186
|
+
# When automatic SSL is enabled, use the Let's Encrypt staging environment
|
|
187
|
+
# for certificate provisioning, so you can test your SSL configuration
|
|
188
|
+
# without running into Let's Encrypt's production rate limits. Certificates
|
|
189
|
+
# issued by the staging environment are not trusted by browsers.
|
|
85
190
|
#
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
forward_headers: true
|
|
191
|
+
# Defaults to `false`:
|
|
192
|
+
ssl_staging: true
|
|
89
193
|
|
|
90
|
-
#
|
|
194
|
+
# Healthcheck
|
|
91
195
|
#
|
|
92
|
-
#
|
|
93
|
-
|
|
196
|
+
# When deploying, the proxy will by default hit `/up` once every second until we hit
|
|
197
|
+
# the deploy timeout, with a 5-second timeout for each request.
|
|
198
|
+
#
|
|
199
|
+
# Once the app is up, the proxy will stop hitting the healthcheck endpoint.
|
|
200
|
+
#
|
|
201
|
+
# By default, the healthcheck is sent to the app port. Set `port` to check a
|
|
202
|
+
# different port on the container, and `host` to set the Host header sent with
|
|
203
|
+
# healthcheck requests.
|
|
204
|
+
healthcheck:
|
|
205
|
+
interval: 3
|
|
206
|
+
path: /health
|
|
207
|
+
timeout: 3
|
|
208
|
+
port: 3001
|
|
209
|
+
host: health.example.com
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# ==========================================================================
|
|
213
|
+
# 2. Traffic & routing
|
|
214
|
+
# ==========================================================================
|
|
94
215
|
|
|
95
216
|
# Path-based routing
|
|
96
217
|
#
|
|
@@ -102,11 +223,13 @@ proxy:
|
|
|
102
223
|
#
|
|
103
224
|
# When using path_prefix you can supply multiple routes separated by commas.
|
|
104
225
|
path_prefix: "/api,/oauth_callback"
|
|
226
|
+
|
|
105
227
|
# You can also specify paths as a list of paths, the configuration will be
|
|
106
228
|
# rolled together into a comma separated string.
|
|
107
229
|
path_prefixes:
|
|
108
230
|
- "/api"
|
|
109
231
|
- "/oauth_callback"
|
|
232
|
+
|
|
110
233
|
# By default, the path prefix will be stripped from the request before it is forwarded upstream.
|
|
111
234
|
#
|
|
112
235
|
# So in the example above, a request to /api/users/123 will be forwarded to web-1 as /users/123.
|
|
@@ -115,16 +238,317 @@ proxy:
|
|
|
115
238
|
# specify --strip-path-prefix=false
|
|
116
239
|
strip_path_prefix: false
|
|
117
240
|
|
|
118
|
-
#
|
|
241
|
+
# Forward headers
|
|
119
242
|
#
|
|
120
|
-
#
|
|
121
|
-
# the deploy timeout, with a 5-second timeout for each request.
|
|
243
|
+
# Whether to forward the `X-Forwarded-For` and `X-Forwarded-Proto` headers.
|
|
122
244
|
#
|
|
123
|
-
#
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
245
|
+
# If you are behind a trusted proxy, you can set this to `true` to forward the headers.
|
|
246
|
+
#
|
|
247
|
+
# By default, kamal-proxy will not forward the headers if the `ssl` option is set to `true`, and
|
|
248
|
+
# will forward them if it is set to `false`.
|
|
249
|
+
forward_headers: true
|
|
250
|
+
|
|
251
|
+
# Header rules
|
|
252
|
+
#
|
|
253
|
+
# Rewrite headers on their way to the app and on their way back out, without
|
|
254
|
+
# the app knowing. `set` replaces whatever was there, `add` appends and keeps
|
|
255
|
+
# it, `remove` strips it.
|
|
256
|
+
#
|
|
257
|
+
# Names are canonicalised, and values have their leading and trailing
|
|
258
|
+
# whitespace trimmed. A value may contain colons — a CSP naming a scheme or a
|
|
259
|
+
# port survives intact.
|
|
260
|
+
#
|
|
261
|
+
# Two things kamal rejects rather than letting them fail late: a value with a
|
|
262
|
+
# newline or carriage return in it (that is response splitting, and shell
|
|
263
|
+
# escaping would silently turn it into a literal backslash-n), and a *request*
|
|
264
|
+
# rule naming `Host` — Go carries the host outside the header map, so the rule
|
|
265
|
+
# would do nothing at all.
|
|
266
|
+
#
|
|
267
|
+
# Response rules apply to what the app returned. Error pages, redirects, and
|
|
268
|
+
# auth or rate-limit rejections come from the proxy itself and are unaffected.
|
|
269
|
+
headers:
|
|
270
|
+
request:
|
|
271
|
+
set:
|
|
272
|
+
X-Forwarded-Host: app.example.com
|
|
273
|
+
add:
|
|
274
|
+
X-Request-Source: kamal
|
|
275
|
+
remove:
|
|
276
|
+
- X-Internal-Token
|
|
277
|
+
response:
|
|
278
|
+
set:
|
|
279
|
+
Strict-Transport-Security: max-age=31536000
|
|
280
|
+
add:
|
|
281
|
+
X-Served-By: kamal-proxy
|
|
282
|
+
remove:
|
|
283
|
+
- Server
|
|
284
|
+
|
|
285
|
+
# Redirects and rewrites
|
|
286
|
+
#
|
|
287
|
+
# A redirect answers the client with a `Location`; a rewrite changes the path
|
|
288
|
+
# the app receives while the client's URL stays as it was — which is what an
|
|
289
|
+
# SPA serving its own routes out of `/index.html` needs.
|
|
290
|
+
#
|
|
291
|
+
# `from` is a regular expression **anchored to the whole path**, so `/old` does
|
|
292
|
+
# not fire on `/not-old-either`. `to` is a path on this host, and for a
|
|
293
|
+
# redirect may also be a full http(s) URL. Captures are available as `$1`,
|
|
294
|
+
# `$2`, … Rules are tried in order and the first match wins.
|
|
295
|
+
#
|
|
296
|
+
# ### Watch the path prefix
|
|
297
|
+
#
|
|
298
|
+
# Both match the path **the client asked for, before `path_prefix` stripping**.
|
|
299
|
+
# An app mounted at `path_prefix: /api` with `strip_path_prefix` on sees
|
|
300
|
+
# `/users`, but a rule here still has to be written against `/api/users`. This
|
|
301
|
+
# is the part that is easy to get wrong.
|
|
302
|
+
#
|
|
303
|
+
# `status` applies to redirects only and must be 301, 302, 303, 307 or 308.
|
|
304
|
+
# It defaults to 301, so say 302 explicitly for anything you may want back.
|
|
305
|
+
redirects:
|
|
306
|
+
- from: /old
|
|
307
|
+
to: /new
|
|
308
|
+
- from: /gone/(.*)
|
|
309
|
+
to: https://elsewhere.example.com/$1
|
|
310
|
+
status: 302
|
|
311
|
+
|
|
312
|
+
rewrites:
|
|
313
|
+
- from: /api/(.*)
|
|
314
|
+
to: /v2/$1
|
|
315
|
+
|
|
316
|
+
# Canonical host
|
|
317
|
+
#
|
|
318
|
+
# Redirect every request to this host, to force apex or www one way.
|
|
319
|
+
canonical_host: www.example.com
|
|
320
|
+
|
|
321
|
+
# Intercept error statuses
|
|
322
|
+
#
|
|
323
|
+
# Replace these statuses coming from the app with the proxy's own error pages,
|
|
324
|
+
# discarding whatever body the app sent. 4xx and 5xx codes only.
|
|
325
|
+
#
|
|
326
|
+
# This pairs with the root-level `error_pages_path`. It works without it, but
|
|
327
|
+
# not usefully: with no pages to render, the proxy falls back to a bare
|
|
328
|
+
# plaintext status line, so the app's own error page is thrown away and
|
|
329
|
+
# replaced by the words "Bad Gateway". Kamal warns when you do that.
|
|
330
|
+
intercept_errors:
|
|
331
|
+
- 502
|
|
332
|
+
- 503
|
|
333
|
+
#
|
|
334
|
+
# ### Note
|
|
335
|
+
# Everything in this section applies on the per-host proxy, including when a
|
|
336
|
+
# loadbalancer is configured — unlike TLS and access control, which move to the
|
|
337
|
+
# loadbalancer. Applying them at both layers would append an `add` header twice
|
|
338
|
+
# and run a rewrite over its own output.
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
# ==========================================================================
|
|
342
|
+
# 3. Security & access
|
|
343
|
+
# ==========================================================================
|
|
344
|
+
|
|
345
|
+
# Basic auth
|
|
346
|
+
#
|
|
347
|
+
# Require HTTP Basic credentials on every request to this service. Requests
|
|
348
|
+
# without valid credentials get a 401 with a `WWW-Authenticate` challenge.
|
|
349
|
+
# The health check path stays open, so deploys are unaffected.
|
|
350
|
+
#
|
|
351
|
+
# Set exactly one of `password_secret` or `password` — never both.
|
|
352
|
+
#
|
|
353
|
+
# Prefer `password_secret`: it names an entry in `.kamal/secrets`, so the
|
|
354
|
+
# password never lives in this file.
|
|
355
|
+
#
|
|
356
|
+
# basic_auth:
|
|
357
|
+
# username: admin
|
|
358
|
+
# password_secret: WEB_BASIC_AUTH_PASSWORD
|
|
359
|
+
#
|
|
360
|
+
# `password` sets the value directly. If you use it, interpolate rather than
|
|
361
|
+
# committing a literal:
|
|
362
|
+
#
|
|
363
|
+
# basic_auth:
|
|
364
|
+
# username: admin
|
|
365
|
+
# password: <%= ENV["WEB_BASIC_AUTH_PASSWORD"] %>
|
|
366
|
+
#
|
|
367
|
+
basic_auth:
|
|
368
|
+
username: admin
|
|
369
|
+
password: <%= ENV["WEB_BASIC_AUTH_PASSWORD"] %>
|
|
370
|
+
password_secret: WEB_BASIC_AUTH_PASSWORD
|
|
371
|
+
#
|
|
372
|
+
# ### Notes
|
|
373
|
+
# - Requires a kamal-proxy that supports `--basic-auth`. This is newer than
|
|
374
|
+
# the current `MINIMUM_VERSION`, so make sure your proxy is up to date
|
|
375
|
+
# before enabling it — an older proxy fails the deploy on an unknown flag.
|
|
376
|
+
# - Basic credentials are replayable and are sent on every request. Use this
|
|
377
|
+
# with `ssl: true`, or terminate TLS in front of the proxy.
|
|
378
|
+
# - kamal-proxy removes the `Authorization` header before forwarding, so a
|
|
379
|
+
# service behind basic auth cannot also pass credentials through to its
|
|
380
|
+
# target. When load balancing, the credentials are enforced by the load
|
|
381
|
+
# balancer only.
|
|
382
|
+
|
|
383
|
+
# Dynamic TLS domains
|
|
384
|
+
#
|
|
385
|
+
# kamal-proxy can learn TLS hostnames from your application at runtime
|
|
386
|
+
# instead of fixing them at deploy time. It polls `source` — a path
|
|
387
|
+
# (resolved against a healthy app target) or an absolute http(s) URL —
|
|
388
|
+
# for the domain list and manages Let's Encrypt certificates for it
|
|
389
|
+
# automatically. When `source` is set, `ssl: true` is allowed without
|
|
390
|
+
# `host`/`hosts`.
|
|
391
|
+
#
|
|
392
|
+
# `interval` is the poll interval in seconds (proxy default: 300).
|
|
393
|
+
#
|
|
394
|
+
# `batch_size` controls how many domains share a certificate: 1 (the
|
|
395
|
+
# proxy default) issues per-domain certs; 2-25 enables stable SAN batching.
|
|
396
|
+
#
|
|
397
|
+
# Authentication tokens for the poll endpoint and the refresh nudge are
|
|
398
|
+
# read by the proxy from the `KAMAL_PROXY_DOMAINS_TOKEN` and
|
|
399
|
+
# `KAMAL_PROXY_REFRESH_TOKEN` environment variables. Set them on the proxy
|
|
400
|
+
# container via `proxy.run.options.env` — never as deploy flags, which
|
|
401
|
+
# leak into process listings and audit logs.
|
|
402
|
+
ssl_domains:
|
|
403
|
+
source: /api/v1/kamal/domains
|
|
404
|
+
interval: 300
|
|
405
|
+
batch_size: 1
|
|
406
|
+
|
|
407
|
+
# Who the client is
|
|
408
|
+
#
|
|
409
|
+
# Rate limiting and IP allow lists are both only as correct as the address they
|
|
410
|
+
# key on, so configure this first if anything sits in front of kamal-proxy.
|
|
411
|
+
#
|
|
412
|
+
# With no `trusted_proxies`, the client is always the address that opened the
|
|
413
|
+
# connection — nothing a client sends can influence it, which is what makes the
|
|
414
|
+
# allow list meaningful.
|
|
415
|
+
#
|
|
416
|
+
# Once you declare `trusted_proxies`, and only when the connecting address is
|
|
417
|
+
# one of them, kamal-proxy reads the forwarded chain instead: it walks the
|
|
418
|
+
# chain from the nearest hop backwards past every proxy you declared, and the
|
|
419
|
+
# first address none of your proxies wrote is the client. **List every hop**,
|
|
420
|
+
# not only the one that connects to kamal-proxy — a chain it cannot resolve
|
|
421
|
+
# denies the request rather than falling back to the connecting address.
|
|
422
|
+
#
|
|
423
|
+
# `header` names the header carrying the original client IP (`CF-Connecting-IP`
|
|
424
|
+
# behind Cloudflare, `True-Client-IP` behind some others); kamal-proxy reads it
|
|
425
|
+
# instead of `X-Forwarded-For`. It is only honoured when `trusted_proxies` is
|
|
426
|
+
# set, because otherwise it is just something the client wrote — kamal rejects
|
|
427
|
+
# that combination rather than appearing to honour it.
|
|
428
|
+
#
|
|
429
|
+
# Addresses and ranges are plain IPv4/IPv6 or CIDR. Write IPv4 as IPv4, not as
|
|
430
|
+
# an IPv4-mapped IPv6 range, and leave IPv6 zones off — neither matches
|
|
431
|
+
# anything. If clients reach you over IPv6, list IPv6 ranges too, or they are
|
|
432
|
+
# denied.
|
|
433
|
+
client_ip:
|
|
434
|
+
header: CF-Connecting-IP
|
|
435
|
+
trusted_proxies:
|
|
436
|
+
- 173.245.48.0/20
|
|
437
|
+
- 2400:cb00::/32
|
|
438
|
+
|
|
439
|
+
# Rate limiting
|
|
440
|
+
#
|
|
441
|
+
# A per-client token bucket. Requests over the limit get a 429. IPv6 clients
|
|
442
|
+
# are counted per /64, since one client can pick any address inside its own.
|
|
443
|
+
#
|
|
444
|
+
# `requests` is requests per second and may be fractional — 0.5 is one request
|
|
445
|
+
# every two seconds. `burst` is how many requests a client may make back to
|
|
446
|
+
# back before the limit applies (default: the rate, rounded up). `exempt` lists
|
|
447
|
+
# addresses and ranges the limit skips, for monitoring and health probes.
|
|
448
|
+
rate_limit:
|
|
449
|
+
requests: 100
|
|
450
|
+
burst: 20
|
|
451
|
+
exempt:
|
|
452
|
+
- 10.0.0.0/8
|
|
453
|
+
|
|
454
|
+
# IP allow list
|
|
455
|
+
#
|
|
456
|
+
# Serve this service only to these addresses and ranges; everything else gets a
|
|
457
|
+
# 403. Combine with `client_ip` above when you are behind a CDN, or the list is
|
|
458
|
+
# matched against the CDN's addresses rather than your visitors'.
|
|
459
|
+
allow_ips:
|
|
460
|
+
- 10.0.0.0/8
|
|
461
|
+
- 192.168.0.0/16
|
|
462
|
+
#
|
|
463
|
+
# ### Notes
|
|
464
|
+
# - The health check path is served without an address check and without a rate
|
|
465
|
+
# limit, so it stays reachable during a deploy. That means it cannot be `/` —
|
|
466
|
+
# kamal rejects `healthcheck: path: /` while either feature is on, because it
|
|
467
|
+
# would leave the whole service open. The default `/up` is fine.
|
|
468
|
+
# - When a loadbalancer is configured, all of this moves to the loadbalancer:
|
|
469
|
+
# the per-host proxies see the loadbalancer as their peer, so an allow list
|
|
470
|
+
# there would refuse every request and one rate limiter would count the whole
|
|
471
|
+
# fleet as a single client.
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
# ==========================================================================
|
|
475
|
+
# 4. Performance & observability
|
|
476
|
+
# ==========================================================================
|
|
477
|
+
|
|
478
|
+
# Two different deadlines
|
|
479
|
+
#
|
|
480
|
+
# `response_timeout` bounds how long the app may take to *start* answering —
|
|
481
|
+
# its clock stops once response headers arrive. `request_timeout` bounds the
|
|
482
|
+
# whole request, including streaming the body back to the client.
|
|
483
|
+
#
|
|
484
|
+
# They are not interchangeable, and the usual confusion is setting the first
|
|
485
|
+
# and still seeing requests hang: a slow trickle of body bytes never trips
|
|
486
|
+
# `response_timeout`, because the app answered promptly. WebSocket and
|
|
487
|
+
# event-stream responses are exempt from `request_timeout`.
|
|
488
|
+
#
|
|
489
|
+
# `response_timeout` defaults to 30 seconds; `request_timeout` defaults to 0,
|
|
490
|
+
# meaning no limit.
|
|
491
|
+
response_timeout: 10
|
|
492
|
+
|
|
493
|
+
request_timeout: 30
|
|
494
|
+
|
|
495
|
+
# Per-path timeouts
|
|
496
|
+
#
|
|
497
|
+
# Override either deadline below a path prefix. Values are Go duration strings
|
|
498
|
+
# ("90s", "5m") or plain seconds; 0 removes the limit for that prefix, which
|
|
499
|
+
# suits streaming and SSE endpoints.
|
|
500
|
+
path_response_timeouts:
|
|
501
|
+
"/api/reports": "5m"
|
|
502
|
+
"/stream": 0
|
|
503
|
+
|
|
504
|
+
path_request_timeouts:
|
|
505
|
+
"/uploads": "10m"
|
|
506
|
+
"/stream": 0
|
|
507
|
+
|
|
508
|
+
# Target connections and retries
|
|
509
|
+
#
|
|
510
|
+
# The connection pool between the proxy and this app's containers, and how
|
|
511
|
+
# hard the proxy tries to place a request on a healthy one.
|
|
512
|
+
#
|
|
513
|
+
# `max_conns` caps simultaneous connections per target, idle ones included.
|
|
514
|
+
# Requests over the cap queue rather than failing, and a streaming response
|
|
515
|
+
# holds its connection until the body closes — so pair a low cap with
|
|
516
|
+
# `request_timeout`. `max_idle_conns` caps the idle connections kept open.
|
|
517
|
+
# Both apply *per pool*, and every entry in `path_response_timeouts` adds another pool.
|
|
518
|
+
#
|
|
519
|
+
# `idle_conn_timeout` is how long an idle connection is kept; set it below the
|
|
520
|
+
# app's own keep-alive timeout, or the proxy will hand a request to a socket
|
|
521
|
+
# the app has already closed. `dial_timeout` bounds establishing a connection
|
|
522
|
+
# — `response_timeout` cannot cover that, since its clock starts only once the
|
|
523
|
+
# request has been written.
|
|
524
|
+
#
|
|
525
|
+
# `try_duration` keeps looking for a healthy target for that long before
|
|
526
|
+
# giving up, with `try_interval` between attempts. Only idempotent requests
|
|
527
|
+
# without a body are re-sent to another target.
|
|
528
|
+
#
|
|
529
|
+
# ### What 0 means here
|
|
530
|
+
#
|
|
531
|
+
# Not the same thing for every key, because the proxy resolves its own
|
|
532
|
+
# defaults from a zero:
|
|
533
|
+
#
|
|
534
|
+
# * `max_conns: 0` — unlimited
|
|
535
|
+
# * `try_duration: 0` — a single attempt, no retrying
|
|
536
|
+
# * `request_timeout: 0` — no limit
|
|
537
|
+
# * `max_idle_conns: 0` — the proxy's default of 100, **not** "keep none"
|
|
538
|
+
# * `idle_conn_timeout: 0` — the proxy's default of 90s
|
|
539
|
+
# * `dial_timeout: 0` — the proxy's default of 30s
|
|
540
|
+
# * `try_interval: 0` — the proxy's default of 250ms
|
|
541
|
+
#
|
|
542
|
+
# So there is no way to ask for zero idle connections; leave the key unset
|
|
543
|
+
# unless you mean to change it.
|
|
544
|
+
target:
|
|
545
|
+
max_conns: 100
|
|
546
|
+
max_idle_conns: 10
|
|
547
|
+
idle_conn_timeout: 90
|
|
548
|
+
dial_timeout: 5
|
|
549
|
+
disable_keep_alives: false
|
|
550
|
+
try_duration: 30
|
|
551
|
+
try_interval: 1
|
|
128
552
|
|
|
129
553
|
# Buffering
|
|
130
554
|
#
|
|
@@ -142,6 +566,113 @@ proxy:
|
|
|
142
566
|
max_response_body: 0
|
|
143
567
|
memory: 2_000_000
|
|
144
568
|
|
|
569
|
+
# Response compression
|
|
570
|
+
#
|
|
571
|
+
# Serve gzip, brotli or zstd without the app knowing about it. Responses the
|
|
572
|
+
# app already encoded, event streams, and media types that do not shrink are
|
|
573
|
+
# passed through untouched.
|
|
574
|
+
#
|
|
575
|
+
# `compress: true` is the common case: it offers zstd, br and gzip in that
|
|
576
|
+
# order — best ratio first, and the client's own `Accept-Encoding` preference
|
|
577
|
+
# still wins — and leaves the length and media-type defaults to the proxy.
|
|
578
|
+
#
|
|
579
|
+
# compress: true
|
|
580
|
+
#
|
|
581
|
+
# The block form is for when you want something narrower. `encodings` names
|
|
582
|
+
# what to offer, most preferred first, from `gzip`, `br` (or `brotli`) and
|
|
583
|
+
# `zstd`; naming it is enough to switch compression on. `min_length` is the
|
|
584
|
+
# response size in bytes below which compressing costs more than it saves
|
|
585
|
+
# (proxy default 1024; set 1 to compress everything).
|
|
586
|
+
#
|
|
587
|
+
# An explicit `enabled: false` switches compression off even when `encodings`
|
|
588
|
+
# are named — it is the off switch for a block whose tuning you want to keep.
|
|
589
|
+
#
|
|
590
|
+
# `content_types` **replaces** the proxy's built-in list of compressible media
|
|
591
|
+
# types rather than adding to it, so name every type you want compressed, not
|
|
592
|
+
# just the extra ones. Entries are exact types or type wildcards. Note that
|
|
593
|
+
# `text/event-stream` is never compressed unless you name it here — holding
|
|
594
|
+
# bytes back to fill the encoder's window is exactly what an event stream's
|
|
595
|
+
# client asked you not to do.
|
|
596
|
+
compress:
|
|
597
|
+
enabled: true
|
|
598
|
+
encodings:
|
|
599
|
+
- zstd
|
|
600
|
+
- br
|
|
601
|
+
- gzip
|
|
602
|
+
content_types:
|
|
603
|
+
- text/html
|
|
604
|
+
- application/json
|
|
605
|
+
min_length: 1024
|
|
606
|
+
|
|
607
|
+
# Response cache
|
|
608
|
+
#
|
|
609
|
+
# An RFC 9111 shared cache in front of this service. Nothing is stored unless
|
|
610
|
+
# the app marks a response `public` with an `s-maxage` or `max-age`, and a
|
|
611
|
+
# response carrying `Set-Cookie` is refused unless `allow_set_cookie` says
|
|
612
|
+
# otherwise — a shared cache replaying one client's cookie to the next is the
|
|
613
|
+
# worst thing it could do.
|
|
614
|
+
#
|
|
615
|
+
# This block is the *policy*, and it is per service. Where the entries live is
|
|
616
|
+
# proxy-wide and set under `run/cache` below.
|
|
617
|
+
#
|
|
618
|
+
# Only `enabled` is required. Everything else keeps kamal-proxy's own default
|
|
619
|
+
# until you set it.
|
|
620
|
+
#
|
|
621
|
+
# `max_ttl` caps the lifetime the app asks for, in seconds, so one mistaken
|
|
622
|
+
# directive cannot pin content until the next deploy. `max_body` is the largest
|
|
623
|
+
# response body to store, in bytes — the same plain byte counts `buffering`
|
|
624
|
+
# uses. Bigger responses still reach the client, they are just not kept.
|
|
625
|
+
#
|
|
626
|
+
# `max_variants` is how many representations one URL may hold when the app
|
|
627
|
+
# negotiates with `Vary` (negative switches automatic variants off and refuses
|
|
628
|
+
# a varying response outright).
|
|
629
|
+
#
|
|
630
|
+
# `vary_headers` and `vary_cookies` add request headers and cookie names to the
|
|
631
|
+
# cache key for EVERY response this service stores. Headers the app already
|
|
632
|
+
# names in `Vary` are keyed automatically per URL and need no entry here —
|
|
633
|
+
# naming one moves it into the key for every path in the service, which is
|
|
634
|
+
# usually not what you want.
|
|
635
|
+
#
|
|
636
|
+
# ### Administering it
|
|
637
|
+
#
|
|
638
|
+
# `kamal proxy cache stats` reports what the cache is holding (add `--count`
|
|
639
|
+
# to measure entries and bytes per service, `--json` for the raw report),
|
|
640
|
+
# and `kamal proxy cache purge` drops this app's cached responses
|
|
641
|
+
# (`--path-prefix /assets` to narrow it). Both run on the layer that owns
|
|
642
|
+
# the cache - the loadbalancer when load balancing, else each proxy host.
|
|
643
|
+
#
|
|
644
|
+
# ### When it is not caching
|
|
645
|
+
#
|
|
646
|
+
# A cache that quietly stores nothing is the usual first surprise. Start with
|
|
647
|
+
# `kamal proxy cache stats`; kamal-proxy also explains every refusal — check
|
|
648
|
+
# `kamal proxy logs` for the reason, and the `cache_refusals_total` metric
|
|
649
|
+
# (by `reason`) if you run with `metrics_port`.
|
|
650
|
+
# The common reasons are a missing `Cache-Control: public, max-age=...` on the
|
|
651
|
+
# app's response, a `Set-Cookie` header, a body over `max_body`, and
|
|
652
|
+
# `variant_limit` from `max_variants`.
|
|
653
|
+
cache:
|
|
654
|
+
enabled: true
|
|
655
|
+
max_ttl: 300
|
|
656
|
+
max_body: 1_048_576
|
|
657
|
+
max_variants: 8
|
|
658
|
+
vary_headers:
|
|
659
|
+
- Accept-Encoding
|
|
660
|
+
- Accept-Language
|
|
661
|
+
vary_cookies:
|
|
662
|
+
- locale
|
|
663
|
+
allow_set_cookie: false
|
|
664
|
+
|
|
665
|
+
# Paths to leave out of the Prometheus metrics
|
|
666
|
+
#
|
|
667
|
+
# Request paths that should not be counted, typically health and readiness
|
|
668
|
+
# endpoints that would otherwise dominate the histograms.
|
|
669
|
+
#
|
|
670
|
+
# This is a per-service deploy setting even though it reads like metrics
|
|
671
|
+
# configuration — where the metrics are served and who may read them are
|
|
672
|
+
# proxy-wide and live under `run/metrics_port` and `run/metrics_allow_ips`.
|
|
673
|
+
exclude_metrics_paths:
|
|
674
|
+
- /up
|
|
675
|
+
|
|
145
676
|
# Logging
|
|
146
677
|
#
|
|
147
678
|
# Configure request logging for the proxy.
|
|
@@ -155,6 +686,88 @@ proxy:
|
|
|
155
686
|
- X-Request-ID
|
|
156
687
|
- X-Request-Start
|
|
157
688
|
|
|
689
|
+
|
|
690
|
+
# ==========================================================================
|
|
691
|
+
# 5. Fleet — multi-host behaviour
|
|
692
|
+
# ==========================================================================
|
|
693
|
+
|
|
694
|
+
# Read-only targets
|
|
695
|
+
#
|
|
696
|
+
# kamal-proxy can split traffic between the deployed (writer) targets and a
|
|
697
|
+
# set of read-only targets, e.g. app instances backed by database replicas.
|
|
698
|
+
# Read requests are routed to the read targets; write requests always go to
|
|
699
|
+
# the writers.
|
|
700
|
+
#
|
|
701
|
+
# Targets are host:port addresses reachable from the proxy.
|
|
702
|
+
#
|
|
703
|
+
# `websockets` routes WebSocket traffic to the read targets too (default
|
|
704
|
+
# `false`). `writer_affinity_timeout` is how long, in seconds, a client's
|
|
705
|
+
# reads stick to the writer after it makes a write, so clients always read
|
|
706
|
+
# their own writes (default 1 second).
|
|
707
|
+
#
|
|
708
|
+
# When a loadbalancer fronts the fleet, read routing is decided there — at
|
|
709
|
+
# the only layer that sees the whole fleet.
|
|
710
|
+
read_routing:
|
|
711
|
+
targets:
|
|
712
|
+
- 192.168.0.2:3000
|
|
713
|
+
- 192.168.0.3:3000
|
|
714
|
+
websockets: true
|
|
715
|
+
writer_affinity_timeout: 10
|
|
716
|
+
|
|
717
|
+
# Session affinity
|
|
718
|
+
#
|
|
719
|
+
# Keep each client on the target that first served it, for apps holding session
|
|
720
|
+
# state in the instance. Off by default, and rightly so — every request is
|
|
721
|
+
# otherwise free to go to whichever target is best placed to serve it.
|
|
722
|
+
#
|
|
723
|
+
# The client carries an opaque HttpOnly cookie naming its target. When that
|
|
724
|
+
# target leaves the pool the next request falls through to another one and is
|
|
725
|
+
# re-pinned, so a deploy does not strand anybody. Reads served by a
|
|
726
|
+
# `read_targets` replica are never pinned.
|
|
727
|
+
#
|
|
728
|
+
# `cookie` renames the pin cookie; kamal-proxy picks a sensible default.
|
|
729
|
+
session_affinity:
|
|
730
|
+
enabled: true
|
|
731
|
+
cookie: _kamal_affinity
|
|
732
|
+
|
|
733
|
+
# Scale to zero
|
|
734
|
+
#
|
|
735
|
+
# Stop this service's containers after `after` seconds with no traffic, and
|
|
736
|
+
# start them again on the next request, which is held until they are healthy.
|
|
737
|
+
# Health checks and the proxy's own TLS probes are not traffic and never wake a
|
|
738
|
+
# sleeping service.
|
|
739
|
+
#
|
|
740
|
+
# `wake_timeout` is how long a request waits for the containers to come back
|
|
741
|
+
# before giving up with a 503. `containers` names the containers to stop and
|
|
742
|
+
# start, replacing what the proxy infers from the target address — needed when
|
|
743
|
+
# a target names a network alias rather than a container.
|
|
744
|
+
#
|
|
745
|
+
# ### This needs the container runtime socket
|
|
746
|
+
#
|
|
747
|
+
# Stopping and starting containers means talking to the runtime, so the proxy
|
|
748
|
+
# must have been booted with `run/docker_socket` set. That is a boot-time
|
|
749
|
+
# prerequisite for a deploy-time setting, so kamal checks it while reading this
|
|
750
|
+
# file rather than letting the first request hang.
|
|
751
|
+
#
|
|
752
|
+
# Setting `run/docker_socket` also mounts that socket into the proxy container
|
|
753
|
+
# — the flag alone only says where to look. **Reaching the container runtime
|
|
754
|
+
# socket is root-equivalent on the host**, which is why it is a separate,
|
|
755
|
+
# explicit setting and not something enabling sleep does for you.
|
|
756
|
+
#
|
|
757
|
+
# Not compatible with `tls/on_demand_url`: a sleeping target cannot answer the
|
|
758
|
+
# ask endpoint, and waking one would let any hostname on the internet start a
|
|
759
|
+
# container.
|
|
760
|
+
sleep:
|
|
761
|
+
after: 300
|
|
762
|
+
wake_timeout: 30
|
|
763
|
+
containers:
|
|
764
|
+
- app-web
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
# ==========================================================================
|
|
768
|
+
# 6. Proxy container (run)
|
|
769
|
+
# ==========================================================================
|
|
770
|
+
|
|
158
771
|
# Run configuration
|
|
159
772
|
#
|
|
160
773
|
# These options are used when booting the proxy container.
|
|
@@ -168,11 +781,218 @@ proxy:
|
|
|
168
781
|
publish: false # Publish ports to the host (default: true)
|
|
169
782
|
bind_ips: # List of IPs to bind to when publishing ports
|
|
170
783
|
- 0.0.0.0
|
|
171
|
-
registry: registry:4443 #
|
|
172
|
-
# (
|
|
173
|
-
|
|
174
|
-
#
|
|
175
|
-
|
|
784
|
+
registry: registry:4443 # Extra registry prefix for the kamal-proxy image
|
|
785
|
+
# (default: none). If you set this, also override
|
|
786
|
+
# `repository` to a host-less path (e.g.
|
|
787
|
+
# myfork/kamal-proxy) - the default repository
|
|
788
|
+
# below already embeds its ghcr.io host
|
|
789
|
+
repository: ghcr.io/mhenrixon/kamal-proxy # Container repository for the
|
|
790
|
+
# kamal-proxy image (this is the default)
|
|
791
|
+
version: v1.0.0.1 # Version tag of the kamal-proxy image to use.
|
|
792
|
+
# Defaults to the minimum version this gem
|
|
793
|
+
# requires - only pin it to roll forward early,
|
|
794
|
+
# never below the default
|
|
795
|
+
port_holder: true # Zero-downtime proxy reboots (default: false).
|
|
796
|
+
# Runs a minimal long-lived kamal-proxy-net container that
|
|
797
|
+
# owns the published ports; proxy generations join its
|
|
798
|
+
# network namespace and overlap during a reboot, so config
|
|
799
|
+
# and version changes apply without dropping requests.
|
|
800
|
+
# Adopting (or leaving) this mode takes one final
|
|
801
|
+
# reboot with a brief gap.
|
|
802
|
+
# ACME / Let's Encrypt
|
|
803
|
+
#
|
|
804
|
+
# Certificate issuance for the whole proxy, including DNS-01 challenges.
|
|
805
|
+
# These are proxy-wide, not per-app: every service on this proxy shares the
|
|
806
|
+
# ACME account and the DNS provider.
|
|
807
|
+
#
|
|
808
|
+
# `dns_provider` names the provider that answers DNS-01 challenges. Leave it
|
|
809
|
+
# at `auto` to let the proxy pick from the credentials it can see. An
|
|
810
|
+
# unsupported name is rejected here, at config time - kamal-proxy would only
|
|
811
|
+
# log a warning and then never issue a certificate.
|
|
812
|
+
#
|
|
813
|
+
# kamal-proxy also accepts short aliases for the canonical names - `cf`
|
|
814
|
+
# (cloudflare), `do` (digitalocean), `gcp`/`google`/`googledns` (gcloud),
|
|
815
|
+
# `gd` (godaddy), `hz` (hetzner), `nc` (namecheap), `aws`/`r53` (route53)
|
|
816
|
+
# and `vr` (vultr). Prefer the canonical name; the aliases exist so a
|
|
817
|
+
# config written for the proxy's own CLI is not rejected here.
|
|
818
|
+
#
|
|
819
|
+
# `prefer_wildcard` asks for a wildcard certificate when the DNS provider
|
|
820
|
+
# supports one, and `http_fallback` falls back to an HTTP-01 challenge when
|
|
821
|
+
# DNS-01 fails. Both default to true in the proxy.
|
|
822
|
+
#
|
|
823
|
+
# `directory` overrides the ACME directory URL - point it at Let's Encrypt's
|
|
824
|
+
# staging environment while you are working out a DNS setup, so you do not
|
|
825
|
+
# burn production rate limits on failed attempts.
|
|
826
|
+
#
|
|
827
|
+
# `credentials` names entries in `.kamal/secrets` to pass to the proxy
|
|
828
|
+
# container as environment variables - the API credentials your DNS provider
|
|
829
|
+
# needs (`CF_API_TOKEN`, `LOOPIA_API_USER`, ...). Name them exactly as the
|
|
830
|
+
# provider expects them, since the name is what reaches the container.
|
|
831
|
+
#
|
|
832
|
+
# They are written to a 0600 env file on the proxy host and passed with
|
|
833
|
+
# `--env-file`, never on the command line: a DNS API token can rewrite your
|
|
834
|
+
# zone, and `docker run --env` would leave it in process listings and audit
|
|
835
|
+
# logs. Adding or renaming one changes the proxy's config digest, so the next
|
|
836
|
+
# deploy reboots the proxy to pick it up; rotating a value does not, so run
|
|
837
|
+
# `kamal proxy reboot` yourself after a rotation.
|
|
838
|
+
acme:
|
|
839
|
+
email: admin@example.com
|
|
840
|
+
dns_provider: cloudflare
|
|
841
|
+
prefer_wildcard: true
|
|
842
|
+
http_fallback: false
|
|
843
|
+
directory: https://acme-staging-v02.api.letsencrypt.org/directory
|
|
844
|
+
credentials:
|
|
845
|
+
- CF_API_TOKEN
|
|
846
|
+
# Logging and tracing
|
|
847
|
+
#
|
|
848
|
+
# `log_format` is the shape of every line the proxy writes, the access log
|
|
849
|
+
# included: `json` (the default) or `text`. `logfmt` is accepted as a name
|
|
850
|
+
# for `text`.
|
|
851
|
+
#
|
|
852
|
+
# `trace_context` decides what happens to the W3C `traceparent` header:
|
|
853
|
+
# `off` ignores it, `propagate` (the default) logs the trace an incoming
|
|
854
|
+
# request carries and forwards the header untouched, and `generate` also
|
|
855
|
+
# starts a trace when a request arrives without one.
|
|
856
|
+
log_format: json
|
|
857
|
+
trace_context: propagate
|
|
858
|
+
|
|
859
|
+
# TLS floor
|
|
860
|
+
#
|
|
861
|
+
# The lowest TLS version the HTTPS listener will negotiate: `1.2` (the
|
|
862
|
+
# default) or `1.3`. Quote it — unquoted YAML reads it as a number.
|
|
863
|
+
#
|
|
864
|
+
# This narrows what the listener accepts and cannot widen it. TLS 1.0 and
|
|
865
|
+
# 1.1 are refused outright, and HTTP/3 is always 1.3 regardless.
|
|
866
|
+
min_tls: "1.2"
|
|
867
|
+
|
|
868
|
+
# HTTP/3
|
|
869
|
+
#
|
|
870
|
+
# Offer HTTP/3 (QUIC) alongside HTTP/1.1 and HTTP/2. Needs UDP on the HTTPS
|
|
871
|
+
# port reachable as well as TCP.
|
|
872
|
+
#
|
|
873
|
+
# Defaults to false:
|
|
874
|
+
http3: true
|
|
875
|
+
|
|
876
|
+
# PROXY protocol
|
|
877
|
+
#
|
|
878
|
+
# Accept PROXY protocol v1/v2 headers on the HTTP and HTTPS listeners, so
|
|
879
|
+
# client addresses survive an L4 load balancer that cannot set
|
|
880
|
+
# `X-Forwarded-For`.
|
|
881
|
+
#
|
|
882
|
+
# **Set `proxy_protocol_allow_ips` with it.** Left empty, the proxy honours a
|
|
883
|
+
# PROXY header from any peer that can reach the port — and that header
|
|
884
|
+
# rewrites the connecting address every other feature keys on, including
|
|
885
|
+
# `allow_ips` and `rate_limit`. Kamal warns when you enable one without the
|
|
886
|
+
# other.
|
|
887
|
+
proxy_protocol: true
|
|
888
|
+
proxy_protocol_allow_ips:
|
|
889
|
+
- 10.0.0.0/8
|
|
890
|
+
|
|
891
|
+
# Metrics access
|
|
892
|
+
#
|
|
893
|
+
# Serve the metrics endpoint (see `metrics_port` above) only to these
|
|
894
|
+
# addresses or CIDR ranges. Empty means everyone who can reach the port.
|
|
895
|
+
#
|
|
896
|
+
# The metrics listener has no service behind it and matches the connecting
|
|
897
|
+
# address only — there is no forwarded chain to trust here, so
|
|
898
|
+
# `proxy/client_ip` does not apply.
|
|
899
|
+
#
|
|
900
|
+
# To leave paths out of the metrics themselves, see
|
|
901
|
+
# `proxy/exclude_metrics_paths`, which is a per-service deploy setting.
|
|
902
|
+
metrics_allow_ips:
|
|
903
|
+
- 10.0.0.0/8
|
|
904
|
+
|
|
905
|
+
# Server timeouts
|
|
906
|
+
#
|
|
907
|
+
# Connection-level deadlines for the proxy's own listeners, in seconds.
|
|
908
|
+
# These are proxy-wide; the per-service deadlines are `response_timeout` and
|
|
909
|
+
# `request_timeout` above.
|
|
910
|
+
#
|
|
911
|
+
# `read_header_timeout` bounds how long a client may take to send request
|
|
912
|
+
# headers. `read_timeout` bounds reading a whole request including its body,
|
|
913
|
+
# so a non-zero value truncates slow uploads. `write_timeout` bounds writing
|
|
914
|
+
# a whole response, so a non-zero value truncates SSE and streaming.
|
|
915
|
+
# `idle_timeout` is how long an idle keep-alive connection is kept.
|
|
916
|
+
#
|
|
917
|
+
# Zero disables each of them, and is a real value rather than "unset".
|
|
918
|
+
#
|
|
919
|
+
# `shutdown_timeout` is how long in-flight requests get to drain when the
|
|
920
|
+
# proxy stops.
|
|
921
|
+
read_header_timeout: 10
|
|
922
|
+
read_timeout: 30
|
|
923
|
+
write_timeout: 30
|
|
924
|
+
idle_timeout: 60
|
|
925
|
+
shutdown_timeout: 30
|
|
926
|
+
|
|
927
|
+
# Boot with an empty routing state when restoring the saved state fails,
|
|
928
|
+
# rather than refusing to start.
|
|
929
|
+
#
|
|
930
|
+
# Defaults to false:
|
|
931
|
+
ignore_restore_errors: true
|
|
932
|
+
|
|
933
|
+
# Bind listeners with SO_REUSEPORT, so an overlapping proxy generation can
|
|
934
|
+
# share the ports during a handoff.
|
|
935
|
+
#
|
|
936
|
+
# Defaults to false:
|
|
937
|
+
reuse_port: true
|
|
938
|
+
|
|
939
|
+
# Escape hatch for `kamal-proxy run`
|
|
940
|
+
#
|
|
941
|
+
# Anything kamal-proxy accepts that has no key above. Note the difference
|
|
942
|
+
# from `options` below, which many people expect to do this: `flags` goes to
|
|
943
|
+
# `kamal-proxy run`, `options` goes to `docker run`.
|
|
944
|
+
#
|
|
945
|
+
# `true` renders a bare flag; anything else renders `--flag value`. No
|
|
946
|
+
# translation is applied, so write Go durations and lists the way
|
|
947
|
+
# kamal-proxy wants them.
|
|
948
|
+
#
|
|
949
|
+
# A key here that a named setting already emits is rejected rather than
|
|
950
|
+
# passed twice — set one or the other.
|
|
951
|
+
flags:
|
|
952
|
+
some-new-flag: value
|
|
953
|
+
|
|
954
|
+
# Container runtime socket
|
|
955
|
+
#
|
|
956
|
+
# Path to the container runtime socket, which is what `proxy/sleep` needs in
|
|
957
|
+
# order to stop and start containers. Setting it does two things: it passes
|
|
958
|
+
# the path to kamal-proxy, and it mounts that path into the proxy container,
|
|
959
|
+
# since the flag alone only says where to look.
|
|
960
|
+
#
|
|
961
|
+
# **Reaching this socket is root-equivalent on the host.** It is a separate,
|
|
962
|
+
# explicit setting for exactly that reason — nothing else in deploy.yml turns
|
|
963
|
+
# it on for you.
|
|
964
|
+
#
|
|
965
|
+
# Defaults to none, with scale-to-zero disabled:
|
|
966
|
+
docker_socket: /var/run/docker.sock
|
|
967
|
+
|
|
968
|
+
# Response cache storage
|
|
969
|
+
#
|
|
970
|
+
# Where the entries cached by `proxy/cache` are kept. Proxy-wide: one store
|
|
971
|
+
# serves every service on this proxy, so unlike the policy above it belongs
|
|
972
|
+
# here, in the run configuration. Adding or removing it reboots the proxy
|
|
973
|
+
# on the next deploy.
|
|
974
|
+
#
|
|
975
|
+
# `store` is `memory` (the default — a per-node cache) or a `redis://` /
|
|
976
|
+
# `rediss://` URL that every proxy pointed at it shares, so one fetch warms
|
|
977
|
+
# the whole fleet.
|
|
978
|
+
#
|
|
979
|
+
# The URL never appears on the proxy's command line: kamal delivers it as
|
|
980
|
+
# `CACHE_STORE` in a 0600 env file on the host (alongside any ACME
|
|
981
|
+
# credentials), so a password in the URL stays out of process listings and
|
|
982
|
+
# kamal's audit log. The trade-off, same as the ACME credentials: changing
|
|
983
|
+
# only the URL's *value* does not move the drift digest — run
|
|
984
|
+
# `kamal proxy reboot` after rotating it.
|
|
985
|
+
#
|
|
986
|
+
# `store_timeout` only matters with a shared store, in seconds: how long
|
|
987
|
+
# the store may take to answer before the request goes to the app instead —
|
|
988
|
+
# a store that is slow or down then costs a cache, never a failed request.
|
|
989
|
+
cache:
|
|
990
|
+
store: redis://cache.example.com:6379/0
|
|
991
|
+
store_timeout: 2
|
|
992
|
+
memory_size: 134_217_728
|
|
993
|
+
# `options` are `docker run` options for the proxy container - resource
|
|
994
|
+
# limits, labels, extra mounts - NOT kamal-proxy flags. A kamal-proxy flag
|
|
995
|
+
# the gem has no key for goes in `flags` above instead.
|
|
176
996
|
options: # Additional options to pass to `docker run`
|
|
177
997
|
label:
|
|
178
998
|
- custom.label=kamal-proxy
|