Skip to content

Commit 290e6db

Browse files
committed
docs(ingress): Publishing service and Managing Caddy
1 parent df29d9b commit 290e6db

File tree

3 files changed

+588
-0
lines changed

3 files changed

+588
-0
lines changed

website/docs/3-concepts/1-ingress/1-overview.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@ When you [publish a service port](2-publishing-services.md), Uncloud automatical
2121
2. Automatically obtain and renew a TLS certificate from Let's Encrypt for HTTPS.
2222
3. Route traffic to the **healthy** service container(s).
2323
4. Load balance across healthy replicas if there are multiple.
24+
25+
For advanced use cases, Uncloud allows to customise the Caddy config using the `x-caddy` extension in Compose files.
26+
See [Custom Caddy configuration](2-publishing-services.md#custom-caddy-configuration) for details.
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
# Publishing services
2+
3+
Publishing service ports makes your services available outside the cluster. This means your services can be accessed
4+
from the internet or local network, depending on your setup.
5+
6+
You can publish service ports in three ways:
7+
8+
- Using the `-p/--publish` flag with `uc run`.
9+
- Using the `x-ports` extension in a Compose file with `uc deploy`.
10+
- Using the `--caddyfile` flag with `uc run` or `x-caddy` extension in a Compose file for custom Caddy configuration.
11+
12+
For example, run a service with container port 8000 exposed as https://app.example.com via Caddy reverse proxy:
13+
14+
```shell
15+
uc run -p app.example.com:8000/https app:latest
16+
```
17+
18+
```
19+
[+] Running service app-mwng (replicated mode) 1/1
20+
✔ Container app-mwng-6lub on machine-fnr9 Started
21+
22+
app-mwng endpoints:
23+
• https://app.example.com → :8000
24+
```
25+
26+
Create an `A` record in your DNS provider (Cloudflare, Namecheap, etc.) pointing `app.example.com` to the public IP
27+
address or your machine(s). Once DNS is propagated and Caddy obtains a TLS certificate, you can access your service
28+
securely over HTTPS.
29+
30+
## Ingress vs host mode
31+
32+
**HTTP/HTTPS** ports are exposed via Caddy using the following format for the `-p/--publish` flag and `x-ports`
33+
extension:
34+
35+
```
36+
[hostname:]container_port[/protocol]
37+
```
38+
39+
- `hostname` (optional): The domain name to use for accessing the service. If omitted and a cluster domain is reserved,
40+
`<service-name>.<cluster-domain>` is used.
41+
- `container_port`: The port number within the container that's listening for traffic.
42+
- `protocol` (optional): `http` or `https` (default: `https`)
43+
44+
**TCP/UDP** ports can only be exposed in host mode, which binds the container port directly to the host machine's
45+
network interface(s). This is useful for non-HTTP services that need direct port access (bypasses Caddy):
46+
47+
```
48+
[host_ip:]host_port:container_port[/protocol]@host
49+
```
50+
51+
- `host_ip` (optional): The IP address on the host to bind to. If omitted, binds to all interfaces.
52+
- `host_port`: The port number on the host to bind to.
53+
- `container_port`: The port number within the container that's listening for traffic.
54+
- `protocol` (optional): `tcp` or `udp` (default: `tcp`)
55+
56+
| Port value | Description |
57+
|------------------------------|--------------------------------------------------------------------------------------|
58+
| `8000/http` | Publish port 8000 as HTTP via Caddy using hostname `<service-name>.<cluster-domain>` |
59+
| `app.example.com:8080/https` | Publish port 8080 as HTTPS via Caddy using hostname `app.example.com` |
60+
| `127.0.0.1:5432:5432@host` | Bind TCP port 5432 to host port 5432 on loopback interface only |
61+
| `53:5353/udp@host` | Bind UDP port 5353 to host port 53 on all network interfaces |
62+
63+
:::warning
64+
65+
Do not publish internal-only services like databases unless absolutely necessary. You only need to publish ports for
66+
services that should be accessible from outside the cluster. Services within the cluster can communicate with each other
67+
by their DNS names `service-name` or `service-name.internal` without publishing ports.
68+
69+
:::
70+
71+
## Using Compose
72+
73+
Use the `x-ports` extension in a Compose file to publish service ports:
74+
75+
```yaml title="compose.yaml"
76+
services:
77+
app:
78+
image: app:latest
79+
x-ports:
80+
- example.com:8000/https
81+
- www.example.com:8000/https # The same port can be published with multiple hostnames
82+
- api.domain.tld:9000/https # Another port can be published with a different hostname
83+
```
84+
85+
## Custom Caddy configuration
86+
87+
For advanced routing and behavior, use `x-caddy` instead of `x-ports`. It allows you to provide custom Caddy
88+
configuration for a service in [Caddyfile](https://caddyserver.com/docs/caddyfile) format.
89+
90+
```yaml title="compose.yaml"
91+
services:
92+
app:
93+
image: app:latest
94+
x-caddy: |
95+
www.example.com {
96+
redir https://example.com{uri} permanent
97+
}
98+
99+
example.com {
100+
basic_auth /admin/* {
101+
admin $2a$14$... # bcrypt hash
102+
}
103+
104+
header /static/* Cache-Control max-age=604800
105+
reverse_proxy {{upstreams 8000}} {
106+
import common_proxy
107+
}
108+
log
109+
}
110+
```
111+
112+
You can inline the Caddyfile or load it from a file: `x-caddy: ./Caddyfile`. When using a file, the path is relative to
113+
the Compose file location. See the [Caddy documentation](https://caddyserver.com/docs/caddyfile) for syntax and
114+
features.
115+
116+
:::info note
117+
118+
You cannot use `x-caddy` with `http` or `https` ports in `x-ports`. `tcp` and `udp` ports in host mode are allowed
119+
though.
120+
121+
:::
122+
123+
Use it when you need:
124+
125+
- Custom routing rules (different paths, redirects, rewrites, multiple services on one domain).
126+
- Custom headers, authentication, or caching.
127+
- Custom load balancing strategies and options.
128+
- Request and response manipulation.
129+
- Advanced TLS settings.
130+
- Other Caddy features and plugins.
131+
132+
See [Deploying or updating Caddy](3-managing-caddy.md#deploying-or-updating-caddy) for details on deploying Caddy with a
133+
custom global configuration.
134+
135+
### Templates
136+
137+
`x-caddy` configs are processed as [Go templates](https://pkg.go.dev/text/template), allowing you to use dynamic values.
138+
The following functions and variables are available:
139+
140+
| Template | Description |
141+
|---------------------------------------|-----------------------------------------------------------------------------------------------|
142+
| `{{upstreams [service-name] [port]}}` | A space-separated list of healthy container IPs for the current or specified service and port |
143+
| `{{.Name}}` | The name of the service the config belongs to |
144+
| `{{.Upstreams}}` | A map of all service names to their healthy container IPs |
145+
146+
The templates are automatically re-rendered and Caddy is reloaded when service containers start/stop or health status
147+
changes.
148+
149+
**Examples:**
150+
151+
1. Current service upstreams, default port:
152+
```caddyfile
153+
reverse_proxy {{upstreams}}
154+
```
155+
156+
157+
```caddyfile
158+
reverse_proxy 10.210.1.3 10.210.2.5
159+
```
160+
2. Current service upstreams, port 8000:
161+
```caddyfile
162+
reverse_proxy {{upstreams 8000}}
163+
```
164+
165+
166+
```caddyfile
167+
reverse_proxy 10.210.1.3:8000 10.210.2.5:8000
168+
```
169+
3. Current service upstreams with `https` scheme:
170+
```caddyfile
171+
reverse_proxy {{- range $ip := index .Upstreams .Name}} https://{{$ip}}{{end}}
172+
```
173+
174+
175+
```caddyfile
176+
reverse_proxy https://10.210.1.3 https://10.210.2.5
177+
```
178+
4. `api` service upstreams, port 9000:
179+
```caddyfile
180+
handle_path /api/* {
181+
reverse_proxy {{upstreams "api" 9000}}
182+
}
183+
```
184+
185+
186+
```caddyfile
187+
handle_path /api/* {
188+
reverse_proxy 10.210.2.2:9000 10.210.1.7:9000 10.210.2.3:9000
189+
}
190+
```
191+
192+
### Verifying Caddy config
193+
194+
Use `uc caddy config` to view the complete generated Caddyfile served by the `caddy` service. This is useful for
195+
debugging and verifying your `x-caddy` configs.
196+
197+
Example output:
198+
199+
```caddyfile
200+
# This file is autogenerated by Uncloud based on the configuration of running services.
201+
# Do not edit manually. Any manual changes will be overwritten on the next update.
202+
203+
# User-defined global config from service 'caddy'.
204+
*.example.com {
205+
tls {
206+
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
207+
}
208+
respond "No host matched" 404
209+
}
210+
211+
# Health check endpoint to verify Caddy reachability on this machine.
212+
http:// {
213+
handle /.uncloud-verify {
214+
respond "a369b9388812f9557feef6a0f5b46f2e" 200
215+
}
216+
log
217+
}
218+
219+
(common_proxy) {
220+
# Retry failed requests up to lb_retries times against other available upstreams.
221+
lb_retries 3
222+
# Upstreams are marked unhealthy for fail_duration after a failed request (passive health checking).
223+
fail_duration 30s
224+
}
225+
226+
# Sites generated from service ports.
227+
228+
https://app.example.com {
229+
reverse_proxy 10.210.1.3:8000 10.210.2.5:8000 {
230+
import common_proxy
231+
}
232+
log
233+
}
234+
235+
https://api.example.com {
236+
reverse_proxy 10.210.2.2:9000 10.210.1.7:9000 10.210.2.3:9000 {
237+
import common_proxy
238+
}
239+
log
240+
}
241+
242+
# User-defined config for service 'web'.
243+
www.example.com {
244+
redir https://example.com{uri} permanent
245+
}
246+
247+
example.com {
248+
reverse_proxy 10.210.0.3:8000 {
249+
import common_proxy
250+
}
251+
log
252+
}
253+
254+
# Skipped invalid user-defined configs:
255+
# - service 'duplicate-hostname': validation failed: adapting config using caddyfile adapter: ambiguous site definition: example.com
256+
# - service 'invalid': validation failed: adapting config using caddyfile adapter: Caddyfile:61: unrecognized directive: invalid_directive
257+
```
258+
259+
The generated config combines:
260+
261+
- Global Caddy configuration (`x-caddy` from the `caddy` service).
262+
See [Deploying or updating Caddy](3-managing-caddy.md#deploying-or-updating-caddy) for details.
263+
- Auto-generated configs from published service ports (`x-ports`).
264+
- Custom Caddy configs from services (`x-caddy`).
265+
- Skipped invalid configs with error messages as comments.
266+
267+
:::warning important
268+
269+
Custom Caddy configs from different services must not conflict (all services must use unique hostnames).
270+
See [Multiple services on one domain](#multiple-services-on-one-domain) for an example of how to share one hostname
271+
between multiple services.
272+
273+
Conflicting or invalid configs are detected using [caddy adapt](https://caddyserver.com/docs/command-line#caddy-adapt)
274+
command and skipped. However, some errors could still break the entire config so Caddy will fail to load it. Check the
275+
`caddy` service logs to troubleshoot.
276+
277+
:::
278+
279+
### Common use cases
280+
281+
#### Redirects
282+
283+
Publish a service on `example.com` and redirect requests from `www.example.com` to `example.com`:
284+
285+
<Tabs>
286+
<TabItem value="compose.yaml">
287+
288+
```yaml
289+
services:
290+
app:
291+
image: app:latest
292+
x-caddy: ./Caddyfile
293+
```
294+
295+
</TabItem>
296+
<TabItem value="Caddyfile">
297+
298+
```caddyfile
299+
www.example.com {
300+
redir https://example.com{uri} permanent
301+
}
302+
303+
example.com {
304+
reverse_proxy {{upstreams 8000}} {
305+
import common_proxy
306+
}
307+
log
308+
}
309+
```
310+
311+
</TabItem>
312+
</Tabs>
313+
314+
#### Multiple services on one domain
315+
316+
You can publish multiple services on the same hostname by using different paths for each service. For example, route
317+
`/` to the web service and `/api` to the API service:
318+
319+
<Tabs>
320+
<TabItem value="compose.yaml">
321+
322+
```yaml
323+
services:
324+
api:
325+
image: api:latest
326+
web:
327+
image: web:latest
328+
# Make sure only one service defines a Caddy config for the hostname.
329+
x-caddy: ./Caddyfile
330+
```
331+
332+
</TabItem>
333+
<TabItem value="Caddyfile">
334+
335+
```caddyfile
336+
example.com {
337+
handle_path /api/* {
338+
reverse_proxy {{upstreams "api" 9000}} {
339+
import common_proxy
340+
}
341+
}
342+
343+
reverse_proxy {{upstreams}} {
344+
import common_proxy
345+
}
346+
347+
log
348+
}
349+
```
350+
351+
</TabItem>
352+
</Tabs>

0 commit comments

Comments
 (0)