Understanding Permissions for the Caddy server

1. Output of caddy version:


2. How I run Caddy:

Installed following the official guide on the host system itself (not Docker), and uses systmctl (as in the docs) rather than manually calling caddy

a. System environment:

Ubuntu 22.04 LTS

3. The problem I’m having:

Users/Permissions. So this may be more a Linux thing - I’m unsure because I don’t understand how Caddy/PHP “run as a user/group”.

I’ve got everything set up and running just fine with PHP and Caddy when their respective config files (and the filesystem) are using the caddy user and group. As long as I sudo chown -R caddy:caddy /webroot it all runs great. PHP can do things, Caddy can serve things.

The issue is that I have to sudo chown -R caddy:caddy /webroot because the User that actually uses the filesystem (myuser lets say) is the one that’s able to SSH in and use GitHub, but if that user owns the /webroot directory, PHP can’t write to it. Conversely if caddy owns it all, GIT can’t write to it. And I can’t “become the caddy user” (and feel like doing that would be wrong anyway)

5. What I already tried:

  • I’ve tried adding myuser to the caddy group.
  • I’ve tried adding caddy to the myuser group.
  • I’ve tried changing the /etc/php/8.1/fpm/pool.d/www.conf file so that the user or group and listen.owner or listen.group belong to myuser (in various combinations)
  • I’ve tried similar with the /lib/systemd/system/caddy.service so the User / Group isn’t just caddy

I have not been able to find a way to get things so that the myuser account can read and write to the /webroot and still retain PHP/Caddy being able to work.

This feels like something of a 101 setup issue, but I’ve also not been able to find anything relevent in the forum of via some googling.

Howdy @MattVCA,

The user and group are only one half of the picture; the ownership. The other half is the actual permissions.

It’s also very much not a Caddy-specific thing but a POSIX operating system thing in general; looking around the forums here might not yield very much historical assistance other than some basic “fix-all” commands. Without knowing all the specific details of your webroot hierarchy and all its permissions, it’s difficult to suggest specific fixes to bring it into alignment with how you want it to function.

Can you tell me which official guide you followed? Our documentation has:

You can place your static site files in either /var/www/html or /srv. Make sure the caddy user has permission to read the files.

But I don’t think it explicitly requires you chown -R caddy:caddy.

Usually with the above instruction we simply mean to add the caddy user to the group owner of the files/directories and then ensuring they’re all group-readable.

Since you’ve got PHP in the mix as well as Caddy, and you want both of them to be able to work with the files as well as the user, I’d propose that perhaps you want the user to have user ownership of the files, and you want a webserver group to have group ownership of the files, and for the files to all be user and group readable and writable.

I would recursively set the user ownership of the web root to myuser and the group ownership to www-data, which the caddy user should be a member of by default:

  • sudo chown -R myuser:www-data /webroot

Then I would ensure that both the user and the group have read-write permissions:

  • find /webroot -type d -exec chmod ug+rwx {} ;
  • find /webroot -type f -exec chmod ug+rw {} ;

Then I personally might consider setting setgid on the directories, which has the side effect of helping to ensure that files created under these directories inherit group ownership of www-data:

  • find /webroot -type d -exec chmod g+s {} ;

Although, if your user copies in files from elsewhere that already has different ownership configured, it may be incumbent on them to chown -R :www-data /webroot on occasion to ensure the web server can access those copied-in files.


Thanks Whitestrake!

I was starting to suspect it was more a Linux owner/permissions thing - but with not understanding 100% what the user and group in both the Caddy and PHP configs were doing (and listen.owner and listen.group especially) I also wasn’t confident I’d got it set up properly there.

Also, my apologies - I’ve recently tried Caddy on two servers and was thinking of the steps I’d followed on the other - this particular one the Caddy stuff was set up “by default” with systemd as it’s Ubuntu 22.04 and “that just works”.

/var/www/html doesn’t exist and /srv seemed weird and owned by root, so I created a /websites directory at root and originally added myuser to the caddy group, and gave /websites a chown myuser:myuser… which didn’t work when i (wrongly) thought it should. So then I chown caddy:caddy /websites and it did work as far as Caddy/PHP were concerned… just I couldn’t then use myuser to do things in it.

I’ll give your advice a try - once someone else is off the server so it’s safe for me to mess about. Thanks again, and I’ll be sure to drop a message to confirm the solution.


Yeah that’s fine, you can still add a directory in there of your choosing and it’ll work fine. Like sudo mkdir /srv/myapp && sudo chown myuser:www-data /srv/myapp or something to that effect.

1 Like

That does appear to have worked, thank you.

For my own understanding though, if anyone is able to clarify further for me - the docs say that the config for Caddy.service should be that it uses the caddy user and the caddy group …


After=network.target network-online.target

ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force


And all of the PHP-FPM tutorials I’ve seen related to Caddy also say that the /etc/php/8.1/fpm/pool.d/www.conf file should explicitly have the www-data default user/group replaced and the listener.owner and listener.group set to caddy

user = caddy
group = caddy
listen = /run/php/php8.1-fpm.sock
listen.owner = caddy
listen.group = caddy

pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3


Does that still hold? Would I be correct in understanding that what these configurations mean is that the Caddy service itself runs under the caddy user and group, and that therefor the PHP-FPM proccess running on the socket should also run as the caddy user and group, and that the listen is… something to do with the socket? That listen bit in particular I do not quite understand.

I also didn’t see anywhere in the docs that explicitly states that the caddy user by default belongs to the www-data group, I may have missed that though.

Yep! You can use these, and revert to them as well if you haven’t. My advice should work for a stock install. That said, if your current configuration works for you - keep it!

These tutorials are probably ‘cargo-culted’ knowledge, because this should not be necessary as the caddy user should be a member of www-data, so having a listener for group www-data should be valid for the Caddy server to connect to, without any additional configuration of PHP.

An Unix socket is an object in the filesystem that acts as a communications bus. It has advantages over an internet socket in that there are no open ports required and access can be managed with file permissions instead, and also that communication is not handled by a network protocol but rather all in-kernel.

You can think of it like an open port, but as a file. You write to the file to communicate with the program that put it there, and you read from the file to listen to that program’s output. This means you need permissions to do so.

When you configure PHP-FPM’s listen variables, you’re telling PHP-FPM where to place this Unix socket when it runs, and what ownership to assign to it. If you leave these alone (they should both be www-data stock, I believe), because the Caddy installation process adds the caddy user to the www-data group, it should be able to access this file by default.

I don’t know if it’s outlined in the docs, but I can link you to the postinstall script in our dist repo where this particular step is undertaken:


Thanks so much for the clear explanation! Much appreciated.

Next time I spin up a server I’ll try things with the defaults first rather than “following the caddy docs”. I have a feeling I’ve been following some advice intended only for the “manual install” when I should’ve just left it alone once I’d added the Debian repo and installed from that.

1 Like

Yeah, by all means!

Time and time again I’ve found with Caddy the vast majority of issues come from people making things more complicated than necessary because the assumption (sometimes even evidenced-backed - such as from other guides across the internet) is that things should be complicated. From all I know, it should be as simple as: install Debian-like distro. Install Caddy and PHP via package manager. Write a Caddyfile. Go! :caddy:


This topic was automatically closed after 30 days. New replies are no longer allowed.