Linux Filesystem & Permissions — The Foundation of Web Hosting
We now move below Nginx and PHP-FPM.
A web server ultimately depends on the Linux operating system being able to answer:
Who is allowed to read, write, execute, create, delete, and access this file?
For your hosting system, this is fundamental.
1. The Hosting Stack So Far
We have reached:
Browser
↓
DNS
↓
IP
↓
TCP
↓
TLS
↓
HTTP
↓
Nginx
↓
PHP-FPM
↓
WordPress
↓
MySQL
Now go one layer deeper:
PHP-FPM
↓
Linux filesystem
↓
Storage
2. Linux Is a Filesystem-Centered System
Linux represents many things through files or filesystem interfaces.
You will constantly work with:
/
├── etc/
├── var/
├── home/
├── usr/
├── run/
├── tmp/
└── storage/
The first:
/
is called:
Root directory
It is the top of the Linux filesystem hierarchy.
3. / Is Not /root
These are completely different.
/
means:
filesystem root
while:
/root
means:
home directory of the
rootuser
This distinction is very important.
4. Your Website Path
Your hosting structure has looked like:
/storage/websites/
Inside:
/storage/websites/
├── domain1.com/
├── domain2.com/
└── domain3.com/
Each domain can have its own directory.
5. Example
Suppose:
/storage/websites/templates.cresignsys.com/public/
contains:
public/
├── index.php
├── wp-admin/
├── wp-content/
├── wp-includes/
└── wp-config.php
This is your WordPress document root.
6. Path
A path identifies where something exists.
For example:
/storage/websites/templates.cresignsys.com/public/index.php
Break it down:
/
└── storage
└── websites
└── templates.cresignsys.com
└── public
└── index.php
7. Absolute Path
A path beginning with:
/
is an absolute path.
Example:
/storage/websites/templates.cresignsys.com/public
It starts from filesystem root.
8. Relative Path
A relative path depends on your current directory.
If you are inside:
/storage/websites/templates.cresignsys.com/
then:
public/index.php
refers to:
/storage/websites/templates.cresignsys.com/public/index.php
9. pwd
To find your current directory:
pwd
Example:
/storage/websites/templates.cresignsys.com/public
pwd means:
Print Working Directory
10. ls
To see files:
ls
More detailed:
ls -l
Including hidden files:
ls -la
11. Example ls -l
You may see:
-rw-r--r-- 1 www-data www-data 1234 Aug 13 index.php
This line contains a lot of information.
Let’s decode it.
12. First Character
-rw-r--r--
^
The first character tells you the object type.
Common examples:
-
regular file.
d
directory.
l
symbolic link.
So:
-rw-r--r--
is a regular file.
13. Permission Characters
After the first character:
rw-r--r--
There are three groups:
rw-
r--
r--
These correspond to:
Owner
Group
Others
14. Read
r
means:
Read
For a file, read means the process can read its contents.
For a directory, read has a different meaning: it allows listing directory entries, subject to other permissions.
15. Write
w
means:
Write
For a file:
w
allows modifying its contents.
For a directory, write is related to creating, deleting, and renaming entries within it.
16. Execute
x
means:
Execute
For an ordinary executable file, it allows execution.
For a directory, x means the process can traverse/access entries within that directory, subject to other permissions.
This distinction is extremely important for web hosting.
17. Three Permission Groups
Consider:
rw-r--r--
Break it:
rw- | r-- | r--
│ │ │
│ │ └── Others
│ └──────── Group
└────────────── Owner
18. Example
-rw-r--r--
means:
Owner:
rw-
Group:
r--
Others:
r--
So:
Owner → read + write
Group → read
Others → read
19. Numeric Permissions
Linux also represents permissions numerically.
The values are:
r = 4
w = 2
x = 1
Add them.
20. 7
rwx
means:
4 + 2 + 1 = 7
21. 6
rw-
means:
4 + 2 = 6
22. 5
r-x
means:
4 + 1 = 5
23. 4
r--
means:
4
24. 0
---
means:
0
25. 755
A very common directory permission:
755
means:
Owner:
7 = rwx
Group:
5 = r-x
Others:
5 = r-x
So:
rwxr-xr-x
26. 644
A common file permission:
644
means:
Owner:
6 = rw-
Group:
4 = r--
Others:
4 = r--
Therefore:
rw-r--r--
27. Why 644 Is Common for Files
A normal web file such as:
index.php
style.css
logo.png
usually needs to be readable by the web server.
It doesn’t normally need to be executable as a Unix program.
Therefore:
644
is often appropriate.
But permissions must always be determined from the actual application and deployment model.
28. Why 755 Is Common for Directories
A directory generally needs:
r
to list entries and:
x
to traverse it.
A common configuration is:
755
allowing the owner full access and others read/traverse access.
Again, the correct permissions depend on the application.
29. Owner
Now consider:
-rw-r--r-- 1 www-data www-data index.php
The first:
www-data
is the owner.
The second:
www-data
is the group.
30. User
Linux identifies users.
Examples:
root
ubuntu
www-data
Each has a user ID.
The kernel uses these identities when enforcing permissions.
31. Group
A user can belong to one or more groups.
Groups allow permissions to be shared among multiple users/processes.
For example:
webadmins
could contain administrators who need access to website files.
32. Root
root is the superuser.
Conceptually:
root
↓
very high system privileges
Root can bypass many ordinary filesystem permission restrictions.
That power must be used carefully.
33. sudo
If you are logged in as:
ubuntu
you may use:
sudo command
to execute a command with elevated privileges if your account is authorized.
For example:
sudo systemctl reload nginx
34. Why sudo Matters
Changing:
/etc/nginx/
usually requires elevated privileges.
But editing your website’s own files may not require root.
A good principle is:
Use the minimum privileges required for the task.
35. www-data
On Debian/Ubuntu web servers, services such as Nginx and PHP-FPM commonly use:
www-data
as their service user, though the exact configuration should be checked.
36. Why www-data Matters
Suppose:
Browser
↓
Nginx
↓
PHP-FPM
and PHP-FPM runs as:
www-data
Then PHP processes access files as that Linux user.
Therefore:
PHP-FPM
↓
Linux permissions
↓
website files
37. WordPress Needs Write Access
WordPress may need to write files for things such as:
uploads
plugin installation
theme installation
updates
generated cache files
temporary files
Whether WordPress should have write access to all of the site is a security/design decision.
Giving the entire WordPress tree broad write access is generally undesirable.
38. Security Principle
Don’t simply do:
chmod -R 777 /storage/websites/example.com
This is a major mistake.
777 means:
Owner = rwx
Group = rwx
Others = rwx
Everyone gets broad read/write/execute access.
39. Why 777 Is Dangerous
Imagine an attacker finds a vulnerability in a website.
If everything is writable:
Attacker
↓
Web application vulnerability
↓
Write anywhere
↓
Upload/modify files
↓
Persistence
The impact can become much worse.
40. Principle of Least Privilege
A fundamental security principle:
Give a process only the permissions it actually needs.
For example:
Static assets
↓
read-only for web server
while:
wp-content/uploads
↓
write access where needed
This creates a smaller attack surface.
41. Directory Permissions Are Different
Suppose:
drwxr-xr-x
The x on a directory means:
Can traverse this directory.
Without directory execute permission, even if a user can read the directory listing, access to files inside can still fail.
42. Example
Imagine:
/storage
↓
/websites
↓
/example.com
↓
/public
The web-service user needs appropriate traversal permissions through every parent directory.
If one parent directory blocks traversal:
/storage
↓
permission denied
Nginx may fail to serve the website.
43. chmod
chmod changes permissions.
Example:
chmod 644 index.php
or:
chmod 755 public
44. Symbolic Permissions
Instead of:
chmod 644 file
you can use:
chmod u=rw,g=r,o=r file
This means:
u = user/owner
g = group
o = others
45. chown
chown changes ownership.
Example:
sudo chown www-data:www-data index.php
This sets:
owner = www-data
group = www-data
46. chgrp
chgrp changes the group without changing the owner.
Example:
sudo chgrp webadmins file.txt
47. Ownership vs Permissions
Don’t confuse these.
Ownership answers:
Who owns this?
Permissions answer:
What can the owner/group/others do?
48. Example
Suppose:
-rw-r-----
root webadmins
Then:
Owner:
root → read/write
Group:
webadmins → read
Others:
no access
49. id
To see your current user and groups:
id
Example:
uid=1000(ubuntu) gid=1000(ubuntu) groups=...
50. Check www-data
You can inspect:
id www-data
This tells you the user ID and group memberships.
51. Find Nginx User
Inspect Nginx configuration:
grep -n "user " /etc/nginx/nginx.conf
You may see something like:
user www-data;
52. Find PHP-FPM User
The PHP-FPM pool configuration determines the worker user.
Depending on PHP version, inspect:
grep -R "^[[:space:]]*user[[:space:]]*=" /etc/php/*/fpm/pool.d/
and:
grep -R "^[[:space:]]*group[[:space:]]*=" /etc/php/*/fpm/pool.d/
You may see:
user = www-data
group = www-data
53. Nginx and PHP-FPM May Both Use www-data
A common architecture is:
Nginx
↓
www-data
PHP-FPM
↓
www-data
This simplifies access to website files.
But it also means you should carefully design write permissions.
54. Website Directory Design
A clean hosting structure could be:
/storage/websites/
│
├── example.com/
│ ├── public/
│ ├── logs/
│ └── private/
│
├── shop.example.com/
│ ├── public/
│ ├── logs/
│ └── private/
│
└── learn.example.com/
├── public/
├── logs/
└── private/
The key principle is:
public/
=
web-accessible area
55. Public vs Private
Suppose:
/storage/websites/example.com/public/
is Nginx’s document root.
Anything inside this directory may potentially be served through HTTP if the Nginx configuration permits it.
Therefore don’t put secrets there.
56. Dangerous Files
Never intentionally expose things such as:
.env
private keys
database backups
SSH keys
application secrets
configuration credentials
through the public document root.
57. WordPress wp-config.php
WordPress has:
wp-config.php
which may contain database credentials and security secrets.
Therefore:
public/wp-config.php
must be protected by the web-server/application architecture.
Modern WordPress/Nginx setups generally prevent direct source disclosure of PHP files because PHP is executed rather than served as plain text, but defense-in-depth still matters.
58. PHP Source Must Not Be Downloadable
If a PHP file were accidentally served as:
text/plain
instead of being processed by PHP-FPM, its source code could be exposed.
Correct architecture:
.php
↓
Nginx
↓
PHP-FPM
↓
execute
not:
.php
↓
Browser downloads source
59. Permissions and WordPress
A typical security-oriented approach is:
WordPress core
↓
mostly read-only
Uploads/cache areas
↓
writeable as required
The exact strategy depends on how you administer WordPress.
60. Linux File Permissions Are Not the Only Layer
A file can be restricted by:
Unix permissions
+
ACLs
+
SELinux/AppArmor
+
Nginx rules
+
PHP-FPM restrictions
+
filesystem mount options
Ubuntu commonly uses AppArmor for some services.
61. ACL
ACL means:
Access Control List
ACLs can provide permissions beyond the simple:
owner
group
others
model.
For example:
user A → read/write
user B → read
group C → read
on the same file.
62. getfacl
To inspect ACLs:
getfacl file.txt
If your server doesn’t use custom ACLs, the output may closely resemble normal permissions.
63. setfacl
ACLs can be changed with:
setfacl
But don’t introduce ACL complexity unless you actually need it.
For a hosting platform, simple ownership/groups are often easier to maintain.
64. Symbolic Links
You may see:
lrwxrwxrwx
The first character:
l
means:
Symbolic link
A symbolic link points to another path.
65. Let’s Encrypt Uses Links
This is particularly relevant to your server.
Certbot commonly maintains:
/etc/letsencrypt/live/domain/
with symbolic links to files under:
/etc/letsencrypt/archive/domain/
Conceptually:
live/
↓
symbolic link
↓
archive/
This is one reason you should not casually delete or replace those files manually.
66. Inspect the Links
Run:
sudo ls -la /etc/letsencrypt/live/templates.cresignsys.com/
You may see entries similar to:
cert.pem -> ../../archive/.../cert1.pem
chain.pem -> ../../archive/.../chain1.pem
fullchain.pem -> ../../archive/.../fullchain1.pem
privkey.pem -> ../../archive/.../privkey1.pem
The exact numbering can vary.
67. Why live/ Exists
Certbot can maintain stable paths:
/etc/letsencrypt/live/domain/fullchain.pem
while the actual versioned certificate files live elsewhere.
Nginx can keep referencing the stable path.
When renewal occurs, the links can be updated to the latest version.
68. Certificate Renewal Connection
Now connect the filesystem lesson to TLS:
Certbot
↓
new certificate
↓
/etc/letsencrypt/archive/
↓
updates /live/ links
↓
Nginx reload
↓
new certificate used
This is why file permissions and symlinks matter to SSL automation.
69. ls -l Is a Core Skill
You should become comfortable reading:
ls -la
because it tells you:
file type
permissions
owner
group
size
timestamp
name
symlink target
70. stat
For deeper information:
stat index.php
You can see:
File
Size
Blocks
IO Block
Device
Inode
Links
Access
Uid
Gid
Access time
Modify time
Change time
71. Inode
Every filesystem object has an:
inode
The inode stores metadata associated with a filesystem object.
Conceptually:
Filename
↓
directory entry
↓
inode
↓
metadata + file data references
72. Filename vs Inode
A filename isn’t the file’s entire identity internally.
The filesystem maintains a relationship:
directory
↓
name → inode
This explains concepts such as hard links.
73. Hard Link
A hard link is another directory entry referring to the same inode.
Conceptually:
file1
│
▼
inode 123
file2
│
▼
inode 123
Both names refer to the same underlying inode.
74. Symbolic Link
A symbolic link is different:
link
↓
path to another object
It doesn’t point directly to the target inode in the same way a hard link does.
75. Why This Matters to Hosting
You will encounter symbolic links in:
Let's Encrypt
PHP configurations
Nginx configurations
Application deployments
Release management
Understanding them prevents many mistakes.
76. Disk Space
Website files ultimately consume storage.
Check:
df -h
This shows filesystem capacity.
Example:
Filesystem
Size
Used
Avail
Use%
Mounted on
77. Directory Size
To inspect a website:
du -sh /storage/websites/templates.cresignsys.com
For more detail:
du -sh /storage/websites/templates.cresignsys.com/*
This helps identify what is consuming disk space.
78. Disk vs Inode Exhaustion
A server can have free disk space but still run out of inodes.
Check:
df -i
This is particularly relevant to hosting servers with huge numbers of small files.
79. Why Hosting Servers Can Have Many Files
WordPress can contain:
Core files
Plugins
Themes
Uploads
Cache
Logs
Temporary files
A server hosting hundreds of sites can therefore contain millions of filesystem objects.
80. Permissions + Storage + Nginx
A web request ultimately becomes:
HTTP
↓
Nginx
↓
Filesystem
↓
Linux kernel
↓
Permission check
↓
Disk
The kernel decides whether the Nginx/PHP process is allowed to perform the operation.
81. Linux Kernel Is the Authority
This is a deep concept.
Nginx does not decide:
www-datais allowed to read this file.
The Linux kernel enforces filesystem access.
Conceptually:
Nginx process
↓
system call
↓
Linux kernel
↓
permission check
↓
filesystem
82. System Call
Programs interact with the kernel through system calls.
For example, a program may request:
open file
read file
write file
The kernel decides whether the operation is permitted.
83. Example
Nginx wants:
read:
/storage/websites/example.com/public/index.html
Conceptually:
Nginx
↓
open()
↓
Linux kernel
↓
check permissions
↓
allow/deny
If denied:
Permission denied
84. This Explains 403
Suppose Nginx receives:
GET /secret.html
but cannot read the file.
The result may become:
403 Forbidden
depending on the configuration and exact reason.
85. This Explains PHP Errors Too
Suppose PHP-FPM tries to write:
wp-content/uploads/
but doesn’t have permission.
Then:
WordPress
↓
PHP-FPM
↓
Linux permission check
↓
DENIED
WordPress may report an upload or filesystem error.
86. Three Different Users
On your server you might have:
ubuntu
root
www-data
They serve different purposes.
ubuntu
Administrative login user.
root
System superuser.
www-data
Web-service/application user.
The exact users on your system should be verified.
87. Why Don’t Run Everything as Root?
If PHP-FPM ran as root:
PHP vulnerability
↓
attacker
↓
root privileges
↓
potential full server compromise
Running services with restricted privileges limits the potential impact.
88. Security Model
A strong hosting architecture tries to separate:
Administrator
↓
root/sudo
Web server
↓
limited service user
Database
↓
database-specific account
Website
↓
restricted filesystem permissions
89. Database User Is Separate
WordPress doesn’t normally connect to MySQL as Linux root.
It uses database credentials stored in its configuration.
Conceptually:
WordPress
↓
MySQL credentials
↓
MySQL user
↓
specific database
This is another layer of least privilege.
90. Linux User vs MySQL User
Don’t confuse:
Linux:
www-data
with:
MySQL:
wordpress_user
They belong to different security systems.
91. Complete Security Boundary
Your website now has several independent layers:
Internet
↓
Cloud firewall
↓
Ubuntu networking
↓
Nginx
↓
TLS
↓
Linux user
↓
Filesystem permissions
↓
PHP-FPM
↓
WordPress
↓
MySQL user
↓
Database permissions
92. Practical Inspection
For your website, start with:
ls -ld /storage
ls -ld /storage/websites
ls -ld /storage/websites/templates.cresignsys.com
ls -ld /storage/websites/templates.cresignsys.com/public
This shows directory permissions and ownership at every level.
93. Inspect WordPress Files
ls -la /storage/websites/templates.cresignsys.com/public
Look for:
index.php
wp-config.php
wp-admin/
wp-content/
wp-includes/
94. Inspect Ownership
sudo stat /storage/websites/templates.cresignsys.com/public/index.php
Look for:
Uid
Gid
Access
95. Test Access as www-data
A very useful diagnostic:
sudo -u www-data ls \
/storage/websites/templates.cresignsys.com/public
This asks:
Can the
www-datauser actually list this directory?
96. Test Reading a File
sudo -u www-data cat \
/storage/websites/templates.cresignsys.com/public/index.php
If permission is denied, you have identified a filesystem-access problem.
Be careful with commands that print sensitive configuration files such as wp-config.php.
97. Test Directory Traversal
You can inspect each parent:
namei -l /storage/websites/templates.cresignsys.com/public/index.php
This is an excellent command.
It shows permissions/ownership along the entire path.
98. Why namei Is Powerful
Suppose:
/storage
is inaccessible to www-data.
Even if:
public/index.php
has:
644
Nginx may still fail.
namei -l helps find the exact blocking directory.
99. The Practical Permission Model
A useful starting principle for a typical WordPress hosting setup is:
Directories
≈ 755
Files
≈ 644
but don’t blindly apply these recursively to every file.
Sensitive files and writable directories may need different permissions.
100. Never Blindly Run Recursive Permission Commands
Avoid blindly doing:
chmod -R 777 ...
or:
chown -R www-data:www-data /
The second could be catastrophic.
Always specify the exact website directory and understand what you’re changing.
101. Your Hosting Platform
For your CresignSys Hosting Platform, a better model is:
/storage/websites/
│
├── site1/
│ ├── public/
│ ├── logs/
│ └── private/
│
├── site2/
│ ├── public/
│ ├── logs/
│ └── private/
│
└── site3/
├── public/
├── logs/
└── private/
Then your automation can consistently create:
directory
ownership
permissions
Nginx configuration
PHP-FPM configuration
SSL configuration
logs
102. Hosting Automation Flow
Eventually your hosting-create script can do:
Create domain
↓
Create directories
↓
Set ownership
↓
Set permissions
↓
Create Nginx config
↓
Test Nginx
↓
Reload Nginx
↓
Create DNS record
↓
Issue Let's Encrypt certificate
↓
Install certificate
↓
Configure HTTPS
↓
Install WordPress
You are now learning the individual layers behind that automation.
103. Deep Architecture
We can now expand the stack:
INTERNET
│
▼
DNS
│
▼
ROUTING
│
▼
TCP/UDP
│
▼
TLS
│
▼
HTTP
│
▼
NGINX
│
┌──────┴──────┐
▼ ▼
FILESYSTEM PHP-FPM
│
▼
WORDPRESS
│
▼
MYSQL
│
▼
STORAGE
And underneath everything:
Linux Kernel
↓
CPU
↓
RAM
↓
Disk/Storage
↓
Network Interface
104. The Next Level
We have now reached an important boundary.
We understand:
DNS
IP
TCP
TLS
HTTP
Nginx
Filesystem
Permissions
The next major component is:
PHP-FPM
because this is the bridge between:
Nginx
and:
WordPress
Lesson 043 Summary
The most important concepts are:
Path
=
location of a filesystem object
Owner
=
user associated with the object
Group
=
group associated with the object
Permissions
=
read/write/execute rules
www-data
=
common web-service user
root
=
superuser
chmod
=
change permissions
chown
=
change ownership
sudo
=
execute with elevated privileges
inode
=
filesystem metadata/object identifier
symlink
=
link pointing to another path
For your hosting server:
Browser
↓
Nginx
↓
PHP-FPM
↓
Linux kernel
↓
Filesystem
Linux permissions determine whether those processes can actually access the files they need.
Next Lesson — 044
PHP-FPM From Zero — How PHP Actually Runs on Your Server
We will trace:
Browser
↓
Nginx
↓
FastCGI
↓
PHP-FPM
↓
PHP worker process
↓
WordPress
↓
MySQL
and go deeply into:
PHP
PHP-FPM
FPM pools
workers
Unix sockets
FastCGI
php.ini
memory limits
execution limits
OPcache
PHP versions
PHP 8.x
process management
www-data
security isolation
This is the next major foundation for understanding why WordPress hosting works.
Leave a Reply