Running letsencrypt as an Unprivileged User

Running letsencrypt as an unprivileged user (non-root) is surprisingly easy, and even more surprisingly undocumented. There is no mention in the official documentation, nor was I able to find anything online. There are alternative clients that were designed to be run as unprivileged, but they are not as beginner-friendly as the official one. Personally, I’ve switched to acme-tiny (and created an AUR package for it). Its much smaller and lets me have an even more secure setup.

Why would you want to bother with this? One word: security. You should always strive to run every process with the lowest privileges possible because this reduces the chances of data loss as a result of a bug. More importantly, this reduces the chances of your server being compromised and thus improves overall security.


In this tutorial we will setup letsencrypt to run as an unprivileged user using the webroot plugin. This tutorial uses basic letsencrypt commands for simplicity. Refer to the official documentation for more advance usage.

Definitions and assumptions:

  • The domain:
  • The web server’s web root: /srv/http/
  • Commands preceded by # should be run as root.
  • Commands preceded by $ should be run as the letsencrypt user.

Prepare the Environment

First we need to create an unprivileged user for letsencrypt; I chose letsencrypt. The following command will create a new system user without a logging shell or a home directory.

# useradd --shell /bin/false --system letsencrypt

Now we will create the needed directory for the webroot plugin, and set the correct permissions.

# mkdir -p /srv/http/
# chown -R letsencrypt: /srv/http/

Optional: verify the web server can serve files created by our user:

$ echo "works!" > /srv/http/
$ curl

If the last command printed “works!”, everything works. Otherwise, something is wrong with your configuration. This is unfortunately out of scope for this tutorial, but feel free to contact me, I might be able tohelp.

Setup letsencrypt

There are two options for this step. The first option is easier, and is best if you already have a working letsencrypt setup. The second option is more correct and is preferred if you are starting fresh.

Option 1: Update the Permissions of the Default Paths

By default letsencrypt (at least on Arch Linux) uses these three paths:

  • logs-dir: /var/log/letsencrypt
  • config-dir: /etc/letsencrypt
  • work-dir: /var/lib/letsencrypt

We need to change these directories to be owned by our user and group:

# chown -R letsencrypt: /var/log/letsencrypt /etc/letsencrypt /var/lib/letsencrypt

Now we need to run letsencrypt so it creates the initial configuration and our first certificate.

$ letsencrypt certonly --webroot -w /srv/http/ -d -d

At this stage letsencrypt will complain about not running as root, that is fine. Ignore it. Just follow the steps and answer the questions.

Option 2: Create New Directories for letsencrypt

Letsencrypt supports a few undocumented flags that let you change the running environment.

First we need to create the relevant directory structure, for simplicity I chose /home/letsencrypt as the base directory and the rest as subdirectories:

# mkdir /home/letsencrypt
# chown letsencrypt: /home/letsencrypt

And as the user:

$ cd /home/letsencrypt
$ mkdir logs config work

Now we can run letsencrypt as we normally do, just with the addition of the --logs-dir, --config-dir and the --work-dir flags.

$ letsencrypt certonly --config-dir /home/letsencrypt/config --work-dir /home/letsencrypt/work \
 --logs-dir /home/letsencrypt/logs/ --webroot -w /srv/http/ -d -d

At this stage letsencrypt will complain about not running as root, that is fine. Ignore it. Just follow the steps and answer the questions.

Verify Functionality

If you got here, you should already have your certificate issued.

You can verify this by running:

Verify Option 1:

$ ls /etc/letsencrypt/live/

Verify Option 2:

$ ls /home/letsencrypt/config/live/

This should output cert.pem chain.pem fullchain.pem privkey.pem

Certificate Renewal

Certificates need to be renewed before they expire or users will receive ominous warnings when visiting your site. You should run letsencrypt in a cron job so the certificate is renewed before it expires (at the time of writing, letsencrypt certificates are valid for 3 months). I have a cron job running once a month.

When renewing, you should run:

Renew Option 1:

$ letsencrypt certonly --agree-tos --renew-by-default --webroot -w /srv/http/ -d \

Renew Option 2:

$ letsencrypt certonly --agree-tos --renew-by-default --config-dir /home/letsencrypt/config \
 --work-dir /home/letsencrypt/work --logs-dir /home/letsencrypt/logs/ --webroot -w /srv/http/ \
 -d -d

Important: do not forget to make the server reload the certificates after they are renewed. Nginx for example, does not do this automatically.

Some Final Notes

For more information about letsencrypt, please refer to the official documentation.

This tutorial does not cover setting up your web server to use the new certificates. This is very simple and covered at length elsewhere.

However, here is an example for nginx:

server {

     ssl_certificate /etc/letsencrypt/live/;
     ssl_certificate_key /etc/letsencrypt/live/;


Letsencrypt is an incredibly important tool in providing better security on the web, so if you have site that doesn’t currently offer HTTPS encryption, I highly encourage you to follow this guide. Please let me know if you encountered any issues or have any suggestions, or feel free to leave a comment on this article.

As originally posted on my blog.

Author: Tom Hacohen

Tom has been a core developer and part of the leading team at SHR (Openmoko), he is currently a core developer for EFL.