The other day i lost the private key to my OpenBSD server and i realized how setting up nginx on OpenBSD is a pain in the ass because of terrible documentation online. I was doing it for the second time, so i thought i’d put it here in case i need it the third time or if some wanderer needs it while setting up theirs.

Setup servers

It might be better to start with the httpd server that is nicely described in the OpenBSD Handbook. Follow the ACME installation. You don’t have to add the cronjob yet. We will add the nginx one at the end.

Your /etc/acme-client.conf should look like this at the end of it.

authority letsencrypt {
        api url ""
        account key "/etc/acme/letsencrypt-privkey.pem"

authority letsencrypt-staging {
        api url ""
        account key "/etc/acme/letsencrypt-staging-privkey.pem"

domain {
       alternative names { }
       domain key "/etc/ssl/private/"
       domain certificate "/etc/ssl/"
       domain full chain certificate "/etc/ssl/"
       sign with letsencrypt

Move to nginx

It’s just easier to use nginx server instead of the pre-packaged httpd on OpenBSD because of better documentation and community.

First, shut down httpd server that could be consuming your port 80 so nginx can use it.

doas rcctl disable httpd
doas rcctl stop httpd

Install nginx. The nginx instructions in the OpenBSD handbook lack details on setting it up with custom domain names. First, get nginx.

export "PKG_PATH=`uname -r`/packages/`arch -s`/"
pkg_add -u nginx 

Your /etc/nginx.conf should like this to serve your content:

worker_processes  1;

worker_rlimit_nofile 1024;
events {
    worker_connections  800;

http {
    include       mime.types;
    default_type  application/octet-stream;
    index         index.html index.htm;

    keepalive_timeout  65;
    server_tokens off;

    server {
        listen       80;
        listen       [::]:80;
        root         /var/www/htdocs/;

	    location /.well-known/acme-challenge/ {
          rewrite ^/.well-known/acme-challenge/(.*) /$1 break;
          root /acme;

        location / {
          return 301 https://$server_name$request_uri;

    server {
        listen       443;
        root         /var/www/htdocs/;
    	access_log   logs/access.log;
    	error_log    logs/error.log info;

	    error_page  404               /404.html;
        location = /404.html {
            root  /var/www/htdocs/;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root  /var/www/htdocs/;

    	location / {
        	    index  index.html;
                autoindex off;

        ssl                  on;
        ssl_certificate      /etc/ssl/;
        ssl_certificate_key  /etc/ssl/private/;

        ssl_session_timeout  5m;
        ssl_session_cache    shared:SSL:1m;

        ssl_ciphers  HIGH:!aNULL:!MD5:!RC4;
        ssl_prefer_server_ciphers   on;

Check if your configuration is right using

# Check if te nginx configuration is right
doas nginx -t

# start / restart / stop nginx
doas rcctl restart nginx

Renew server certs

Automatically in 90 days

05 3 * * * acme-client && rcctl reload nginx

Voila! Happy OpenBSD and nginx.

[Bonus] Setup rsync

  1. Install rsync

    doas pkg_add rsync
  2. Set up a new user for rsync

    doas useradd -m -s /sbin/nologin rsync
  3. Create a new config file for rsync daemon

    sudo touch /etc/rsyncd.conf
    sudo chmod 600 /etc/rsyncd.conf
    sudo chown root:_rsync /etc/rsyncd.conf

    This creates a new configuration file at /etc/rsyncd.conf with restricted permissions that can be read by the rsync user.

    uid = rsync
    gid = _rsync
    log file = /var/log/rsyncd.log
    pid file = /var/run/
  4. From your local host, rsync away:

    rsync -avz --delete {LOCAL_DIR_TO_COPY}/ ${HOSTNAME}:${REMOTE_DIR}