Skip to content

Commit 20e5fac

Browse files
committed
Add Caddy note
1 parent da1cc8b commit 20e5fac

1 file changed

Lines changed: 261 additions & 0 deletions

File tree

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
---
2+
title: "How To: Caddy (on OpenBSD)"
3+
dateCreated: 2025-12-17
4+
dateUpdated: 2025-12-17
5+
---
6+
7+
## Preamble
8+
9+
### Aims
10+
11+
To document the steps I recently took to set up Caddy on an OpenBSD server, so I
12+
don't end up forgetting them.
13+
14+
The steps outlined below specifically show how to host a single static site
15+
under one domain. However, in general, whether you're using Caddy to serve
16+
multiple static sites, your PHP app, or an API as a reverse proxy, most of these
17+
steps are more or less the same steps you'd take.
18+
19+
### Assumptions
20+
21+
- Familiarity with Unix-like operating systems and web servers
22+
- Some understanding of networking
23+
- An already setup OpenBSD server
24+
- Static assets go in `/var/www/htdocs` and web server logs go in
25+
`/var/www/logs`
26+
27+
### Caveats
28+
29+
I'm pretty new to OpenBSD and Caddy, so there may be errors in this document.
30+
You should consider this a resource, not an ultimate source of truth. When in
31+
doubt, the official documentation and man pages referenced below are your
32+
friends.
33+
34+
As an aside, base OpenBSD already comes with an HTTP server, httpd, and a proxy,
35+
relayd, so you might not even want to use Caddy on OpenBSD to begin with.
36+
37+
## Steps
38+
39+
1. Install Caddy:
40+
41+
```console
42+
pkg_add caddy
43+
```
44+
45+
2. Put static assets on server:
46+
47+
Below are some commands you can run to transfer local files to your server.
48+
They assume you're using an admin user, not root.
49+
50+
Use SCP to transfer the files:
51+
52+
```console
53+
scp -r /path/to/files/source <username>@<ip>:/path/to/files/destination
54+
```
55+
56+
Once on the server, move them to `/var/www/htdocs`:
57+
58+
```console
59+
doas mv /path/to/files /var/www/htdocs/<domain>
60+
```
61+
62+
Then, follow `/var/www/htdocs` conventions by transferring ownership to root:
63+
64+
```console
65+
doas chown -R root:wheel /var/www/htdocs/<domain>
66+
```
67+
68+
Lastly, make sure that they're readable but not writable by other users:
69+
70+
```console
71+
doas chmod -R g+rX-w,o+rX-w /var/www/htdocs/<domain>
72+
```
73+
74+
**Note:** These are only example commands, maybe you want to transfer the
75+
files using rsync or you may want to automate the whole thing via CI/CD, you
76+
do you.
77+
78+
3. Create log directory for Caddy:
79+
80+
```console
81+
doas mkdir /var/www/logs/<domain>
82+
```
83+
84+
4. Transfer ownership of log directory to Caddy:
85+
86+
```console
87+
doas chown _caddy:_caddy /var/www/logs/<domain>
88+
```
89+
90+
5. Support `configtest` action:
91+
92+
```console
93+
doas vi `/etc/rc.d/caddy`
94+
```
95+
96+
Then, add the following before the `rc_start` function:
97+
98+
```shell
99+
rc_configtest() {
100+
rc_exec "${daemon} validate ${daemon_flags}"
101+
}
102+
```
103+
104+
**Note:** This is optional but `configtest` is useful when making sure that
105+
your config is valid before starting or reloading Caddy.
106+
107+
6. Update Caddy config:
108+
109+
```console
110+
doas vi /etc/caddy/Caddyfile
111+
```
112+
113+
Then, update the contents to something like below:
114+
115+
```text
116+
{
117+
# Bind to server's IP
118+
default_bind <ip>
119+
120+
# Use custom ports since non-root users can't bind to ports below 1024
121+
# Standard ports will be exposed publicly
122+
# Custom ports will only be used internally
123+
# PF will handle standard to custom ports translation
124+
http_port <http-port>
125+
https_port <https-port>
126+
127+
# Use of private custom ports require HTTPS redirection to be done manually
128+
auto_https disable_redirects
129+
130+
# Limit admin endpoint access by using Unix socket instead of localhost
131+
admin unix//var/caddy/admin.sock|0220
132+
133+
# Caddy doesn't support installing internal CA on OpenBSD
134+
skip_install_trust
135+
136+
# Configure global log file
137+
log {
138+
output file /var/www/logs/<domain>/default.log
139+
}
140+
}
141+
142+
# Configure HTTP requests to log and redirect to HTTPS
143+
<domain>:<http-port> {
144+
log
145+
146+
# Redirect client to standard port since PF will handle port translation
147+
redir https://<domain>{uri} permanent
148+
}
149+
150+
# Configure HTTPS requests to log and serve files
151+
<domain>:<https-port> {
152+
log
153+
154+
root * /var/www/htdocs/<domain>
155+
file_server
156+
}
157+
```
158+
159+
**Note:** This is a minimal config, as such, you may want to extend it by
160+
handling www redirections, having a catchall handler, or logging to different
161+
log files instead of just one. The sky's the limit.
162+
163+
7. Verify Caddy config validity:
164+
165+
```console
166+
rcctl configtest caddy
167+
```
168+
169+
**Note:** This will only work if you did step 5, therefore it is also
170+
optional.
171+
172+
8. Set environment variables for Caddy:
173+
174+
```console
175+
doas vi /etc/login.conf.d/caddy
176+
```
177+
178+
Then, put the following Caddy class:
179+
180+
```text
181+
caddy:\
182+
:setenv=CADDY_VAR1=value_one,CADDY_VAR2=value_two:\
183+
:tc=daemon:
184+
```
185+
186+
**Note:** This is only needed if you want to set environment variables for
187+
your Caddy config. `setenv` sets the comma-separated key-value pairs as
188+
environment variables and `tc` extends the `daemon` class.
189+
190+
9. Update PF config:
191+
192+
```console
193+
doas vi /etc/pf.conf
194+
```
195+
196+
Then, add the following rules:
197+
198+
```text
199+
# Redirect standard ports to custom ports set in Caddy config
200+
pass in on egress proto tcp to egress port 80 rdr-to egress port <http-port>
201+
pass in on egress proto tcp to egress port 443 rdr-to egress port <https-port>
202+
```
203+
204+
10. Verify PF config validity:
205+
206+
```console
207+
doas pfctl -nf /etc/pf.conf
208+
```
209+
210+
11. Point your domain's A or AAAA record to your server's IP
211+
212+
**Note:** It's important for this to be done before opening the floodgates
213+
and starting Caddy so that TLS certificates can be provisioned successfully.
214+
215+
12. Apply updated PF config:
216+
217+
```console
218+
doas pfctl -f /etc/pf.conf
219+
```
220+
221+
13. Check active PF rules:
222+
223+
```console
224+
doas pfctl -sr
225+
```
226+
227+
14. Start Caddy:
228+
229+
```console
230+
rcctl start caddy
231+
```
232+
233+
15. Enable Caddy:
234+
235+
```console
236+
rcctl enable caddy
237+
```
238+
239+
**Note:** This ensures that Caddy will be restarted after server reboots.
240+
241+
## References
242+
243+
- <https://caddyserver.com/docs>
244+
- <https://man.openbsd.org/httpd.8>
245+
- <https://man.openbsd.org/httpd.conf.5>
246+
- <https://man.openbsd.org/relayd.8>
247+
- <https://man.openbsd.org/pkg_add.1>
248+
- <https://man.openbsd.org/scp.1>
249+
- <https://man.openbsd.org/mv.1>
250+
- <https://man.openbsd.org/chown.8>
251+
- <https://man.openbsd.org/chmod.1>
252+
- <https://man.openbsd.org/mkdir.1>
253+
- <https://man.openbsd.org/rcctl.8>
254+
- <https://man.openbsd.org/rc.d.8>
255+
- <https://man.openbsd.org/login.conf.5>
256+
- <https://github.com/openbsd/ports/tree/master/www/caddy>
257+
- <https://github.com/openbsd/src/blob/master/etc/rc.d/httpd>
258+
- <https://github.com/openbsd/src/blob/master/etc/rc.d/rc.subr>
259+
- <https://man.openbsd.org/pf.conf.5>
260+
- <https://fabiolb.net/faq/binding-to-low-ports/#openbsdfreebsdnetbsd>
261+
- <https://man.openbsd.org/pfctl.8>

0 commit comments

Comments
 (0)