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:
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.