Skip to main content

Understanding your sites configuration layout

In this lesson I'm going to show you my goto site configuration layout, which allows me to keep things well organized and under version control, as well as move sites between servers when needed. It is not a hard requirement to follow this directory structure and you should build on it and adapt it based on your needs.

We already have a /config directory with our server configuration under version control. This will also have some site-specific configuration for things like domains, PHP pools, etc. However, the sites themselves (application files, themes, plugins, uploads, etc.) should live in a different place.

I like to put them into /sites.

The /sites/ directory

Inside this sites directory, I put each WordPress site into its own directory, typically using its primary domain as the name.

Even if you do not intend to host multiple WordPress sites on this server right now, this per-site separation is still a good idea for having staging sites, for adding more sites in the future, and for migrating this and other sites to and from this server.

Here's a typical site layout I like to use:

sites/
├─ uncached.org/
│  ├─ public_html/
│  ├─ backups/
│  ├─ logs/
│  └─ tmp/
├─ staging.uncached.org/
└─ dev.uncached.org/

You don't need version control for the sites directory, unless you'd like to version control the individual sites themselves, which we'll cover in a CI/CD advanced module.

The public_html directory will have all our WordPress application files, and everything else that's public. It's a fairly old naming convention, and one that many people have come across. If you don't like it, some alternative names are www or simply public.

The backups directory will contain on-site database and file backups for this specific site. We'll cover backups in detail in module 9.

The logs directory is where we'll dump all our logs specific to this site, including Nginx access and error logs, PHP pool and error logs, and even logs coming from WordPress itself, such as the WP_DEBUG_LOG, WooCommerce logs, and more. If you've been following my previous lessons, hopefully you're thinking "logrotate!" right now.

The tmp directory will be configured as the temporary, uploads, and sessions directory for this site. This helps reduce the number of shared directories each individual site can use, and minimizes the attack surface and damage if one of the sites gets compromised.

Ownership and permissions

To keep things as simple as possible, we're going to start by having everything owned by the www-data user, which is available on Ubuntu and other systems. This user will be used in Nginx and PHP workers, so both will be able to access all site data.

The tradeoff for this simplicity is some security, leaving some potential for one compromised site to access files of another on the same system. We'll still have per-site restrictions for PHP pools, however vulnerabilities in PHP modules, Nginx, or Nginx modules could result in a bypass of these restrictions.

I'll cover more security practices in future lessons and full isolation using containers in an advanced module. For now, let's go for a good balance between security and simplicity:

sudo mkdir -p /sites/uncached.org/{public_html,backups,logs,tmp}
sudo chown -R www-data:www-data /sites/uncached.org

The sites directory itself will be owned by root, however each individual site will be owned by www-data, allowing Nginx and PHP to access application files, write logs, and more.

The permissions should remain at the default 0755 for directories and 0644 for files.

ACLs

Since I use the system as the karl user, I would also like to grant myself access to all these files and directories without having to sudo every time I want to edit something owned by www-data.

This can be done using setfacl, a tool to manage POSIX ACLs (Access Control Lists). This tool may already be installed on your system, but if not, you can install it with your favorite package manager:

sudo apt update
sudo apt install acl

Now we can set ACLs for the /sites directory:

sudo setfacl -mR u:karl:rwX /sites
sudo setfacl -mRd u:karl:rwX /sites

The first command modifies (-m) the ACL for the /sites directory recursively (-R) and grants the user karl read-write-execute (rwX) access. The second command is similar, but the default (-d) flag ensures that new files and directories will inherit this ACL.

You can use getfacl to display the ACL for a specific file or directory:

Linux ACLs getfacl

Now the user karl can access everything in the /sites directory, however if you create a new file under this user, it will still be owned by karl and the user's default group. While the www-data user will still be able to read this file, it will not be able to write to it. This may become a problem, for example when updating or installing a WordPress plugin using WP-CLI. Any update from the wp-admin dashboard will fail, because those will be attempted as the www-data user.

Unless you explicitly intend for the new files/directories to be owned by your current user, it's best to reset their ownership and group to www-data using chown, for example:

sudo chown www-data:www-data \
    /sites/uncached.org/public_html/test.php

Or for an entire directory recursively, using -R:

sudo chown -R www-data:www-data \
    /sites/uncached.org/public_html/wp-content/plugins

Whatever you do, do not chmod anything to 777. It is not your lucky number. It is bad Internet advice which "solves" a lot of permission problems by allowing everything to everyone. If you're having any permissions trouble, please don't hesitate to ask in our Discord community.

Enroll
Enjoying the course content? Enroll today to keep track of your progress, access premium lessons and more.