WordPress Installation Automation — From Empty Domain to Working Website
We now combine the previous lessons into a complete hosting workflow.
The objective is to understand how a new domain goes from:
Empty domain
↓
DNS
↓
Server
↓
Nginx
↓
PHP-FPM
↓
MySQL
↓
WordPress
↓
SSL
↓
Working website
1. The Final Architecture
For one website:
learn.cresignsys.com
│
▼
DNS
│
▼
Public IP address
│
▼
Nginx
│
▼
PHP-FPM pool
│
▼
WordPress
│
▼
MySQL
The filesystem is:
/storage/websites/
└── learn.cresignsys.com/
└── public/
2. What We Want to Automate
Instead of manually performing 15–20 commands for every website, we want:
sudo hosting-create learn.cresignsys.com
to eventually perform the provisioning process.
Conceptually:
hosting-create
│
├── create user
├── create directories
├── set permissions
├── create database
├── create database user
├── create PHP-FPM pool
├── create Nginx config
├── validate Nginx
├── reload services
├── download WordPress
├── configure wp-config.php
└── install SSL
3. Step 1 — Domain
Start with:
learn.cresignsys.com
Your hosting system should first validate the domain.
For example:
DOMAIN=learn.cresignsys.com
4. Generate a Site ID
Don’t blindly use arbitrary domain text as a Linux username.
Generate a safe internal identifier.
For example:
learn_cresignsys_com
You can use this internally for:
Linux user
PHP-FPM pool
database naming
logging
configuration
5. Site Configuration
A site record could conceptually contain:
DOMAIN:
learn.cresignsys.com
SITE_ID:
learn_cresignsys_com
WEB_ROOT:
/storage/websites/learn.cresignsys.com/public
LINUX_USER:
learn_cresignsys_com
DATABASE:
learn_cresignsys_com_db
DATABASE_USER:
learn_cresignsys_com_dbuser
Your actual naming scheme can be shorter.
6. Step 2 — Create Directory
Create:
sudo mkdir -p /storage/websites/learn.cresignsys.com/public
Now:
/storage/websites/
└── learn.cresignsys.com/
└── public/
7. Why mkdir -p?
The:
-p
option creates missing parent directories as necessary.
Therefore:
mkdir -p /storage/websites/domain.com/public
can create the entire directory path if it doesn’t already exist.
8. Step 3 — Create Linux User
Create a dedicated hosting identity.
Conceptually:
sudo useradd ...
The exact account configuration should match your hosting architecture.
For example:
site user
↓
website files
↓
PHP-FPM pool
9. Step 4 — Ownership
Set the website ownership appropriately.
For example:
sudo chown -R siteuser:siteuser /storage/websites/learn.cresignsys.com
Then verify:
ls -la /storage/websites/learn.cresignsys.com
10. Step 5 — Directory Permissions
Start with a restrictive model.
For example:
sudo chmod 755 /storage/websites/learn.cresignsys.com
sudo chmod 755 /storage/websites/learn.cresignsys.com/public
Do not use:
chmod -R 777
11. Step 6 — MySQL Database
Create a database:
CREATE DATABASE learn_cresignsys_com_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
12. Step 7 — Database User
Create a dedicated user:
CREATE USER 'learn_dbuser'@'localhost'
IDENTIFIED BY 'STRONG_RANDOM_PASSWORD';
Use a generated random password in automation rather than a hard-coded password.
13. Step 8 — Database Privileges
Grant access only to this database:
GRANT ALL PRIVILEGES
ON learn_cresignsys_com_db.*
TO 'learn_dbuser'@'localhost';
The important relationship is:
learn_dbuser
↓
learn_cresignsys_com_db
not:
learn_dbuser
↓
ALL DATABASES
14. Step 9 — PHP-FPM Pool
Create a dedicated PHP-FPM pool.
Conceptually:
site:
learn
user:
siteuser
group:
siteuser
socket:
/run/php/learn.sock
15. Example Pool
A simplified configuration:
[learn]
user = siteuser
group = siteuser
listen = /run/php/learn.sock
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500
These values are examples for learning, not universal production settings.
16. Why Dedicated Pool?
The request becomes:
Nginx
↓
learn.sock
↓
learn PHP-FPM pool
↓
siteuser
↓
WordPress
This connects:
domain
+
PHP
+
Linux identity
17. Step 10 — Nginx Server Block
Create:
/etc/nginx/sites-available/learn.cresignsys.com
A simplified configuration:
server {
listen 80;
server_name learn.cresignsys.com;
root /storage/websites/learn.cresignsys.com/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/learn.sock;
}
}
This is an educational baseline. Production configurations should be hardened and adapted to the PHP-FPM version and your exact Nginx layout.
18. Understand the Server Block
This line:
server_name learn.cresignsys.com;
means:
Request for learn.cresignsys.com
↓
this server block
19. Document Root
This:
root /storage/websites/learn.cresignsys.com/public;
means the web root is:
/storage/websites/learn.cresignsys.com/public
20. WordPress Rewrite
This:
try_files $uri $uri/ /index.php?$args;
is critical.
For:
/about/
Nginx can eventually send the request to:
index.php
and WordPress determines which page should be returned.
21. PHP Routing
This:
location ~ \.php$
matches PHP requests.
Then:
fastcgi_pass unix:/run/php/learn.sock;
sends PHP processing to:
learn PHP-FPM pool
22. Step 11 — Enable Site
On systems using sites-available / sites-enabled, create the symbolic link:
sudo ln -s /etc/nginx/sites-available/learn.cresignsys.com \
/etc/nginx/sites-enabled/learn.cresignsys.com
Your exact Nginx installation may instead use another configuration directory.
23. Step 12 — Validate Nginx
Never immediately reload after generating a configuration.
First:
sudo nginx -t
You want:
syntax is ok
test is successful
24. Step 13 — Reload Nginx
If validation succeeds:
sudo systemctl reload nginx
A reload normally allows Nginx to apply the new configuration without requiring a full machine reboot.
25. Step 14 — Restart/Reload PHP-FPM
After adding a new pool, reload the relevant PHP-FPM service.
For example:
sudo systemctl reload php8.3-fpm
Use the PHP version actually installed on your server.
26. Check the PHP Socket
Run:
ls -l /run/php/
You should find your socket, for example:
learn.sock
If it doesn’t exist:
Nginx
↓
missing socket
↓
502
27. Step 15 — Test Nginx
Before installing WordPress, create a simple test file:
echo "CresignSys OK" | sudo tee \
/storage/websites/learn.cresignsys.com/public/index.html
Then:
curl -I http://learn.cresignsys.com
If everything is correct:
HTTP/1.1 200 OK
28. Browser Test
Open:
http://learn.cresignsys.com
You should see:
CresignSys OK
This confirms:
DNS
↓
Nginx
↓
document root
↓
static file
are working.
29. Remove the Test File
Once confirmed:
sudo rm /storage/websites/learn.cresignsys.com/public/index.html
Now the directory is ready for WordPress.
30. Step 16 — Download WordPress
From the site’s public directory:
cd /storage/websites/learn.cresignsys.com/public
Then download WordPress.
A standard approach is:
sudo -u siteuser wp core download
if WP-CLI is installed and configured for your environment.
31. Why Use WP-CLI?
WP-CLI allows you to automate WordPress management from the command line.
Instead of:
browser
↓
WordPress installation wizard
you can use:
terminal
↓
WP-CLI
↓
WordPress
This is ideal for hosting automation.
32. WP-CLI Installation
Check:
wp --info
You may see:
OS:
PHP binary:
PHP version:
WP-CLI version:
If WP-CLI isn’t installed, install it separately before building the provisioning script.
33. Step 17 — Create wp-config.php
WP-CLI can generate configuration.
Conceptually:
wp config create \
--dbname=learn_cresignsys_com_db \
--dbuser=learn_dbuser \
--dbpass='PASSWORD' \
--dbhost=localhost
This generates:
wp-config.php
with the database connection information.
34. Why wp-config.php Matters
It connects:
WordPress
↓
MySQL
It contains information such as:
DB_NAME
DB_USER
DB_PASSWORD
DB_HOST
It also contains WordPress authentication salts and other configuration.
35. Don’t Put Passwords in Shell History
Be careful with commands such as:
--dbpass='password'
because secrets can accidentally become visible through:
shell history
process listings
logs
scripts
A production provisioning system should handle secrets carefully.
36. Step 18 — Install WordPress
You can use WP-CLI:
wp core install \
--url='https://learn.cresignsys.com' \
--title='Learn' \
--admin_user='admin' \
--admin_password='STRONG_PASSWORD' \
--admin_email='admin@example.com'
Again, do not hard-code production credentials.
37. Better Admin Username
Avoid using:
admin
as the primary administrator username when creating a new WordPress installation.
Use a unique administrator account name.
38. Strong Password
Use a generated password rather than:
admin123
or:
password
A hosting platform should generate strong credentials automatically.
39. WordPress Installation Flow
At this point:
Nginx
↓
PHP-FPM
↓
WordPress
↓
MySQL
should be functional.
40. Step 19 — Check WordPress
Run:
wp core is-installed
A successful installation should report that WordPress is installed.
You can also:
wp option get siteurl
and:
wp option get home
41. Check Database Connection
Run:
wp db check
If successful, WP-CLI should confirm the database connection.
This is a very useful provisioning test.
42. Step 20 — Permissions
After WordPress installation:
sudo chown -R siteuser:siteuser \
/storage/websites/learn.cresignsys.com
Then apply your intended directory/file permissions.
Avoid blindly applying one recursive mode to everything.
43. WordPress File Model
Conceptually:
public/
├── index.php
├── wp-admin/
├── wp-includes/
├── wp-content/
│ ├── plugins/
│ ├── themes/
│ └── uploads/
└── wp-config.php
44. Important Files
index.php
Entry point.
wp-config.php
Database and core configuration.
wp-content/
Site-specific content.
wp-admin/
Administration interface.
wp-includes/
WordPress core libraries.
45. Step 21 — DNS
Now your domain needs to point to your server.
For:
learn.cresignsys.com
you need the appropriate DNS record.
For example:
A
learn.cresignsys.com
→
YOUR_SERVER_IP
If using IPv6, an appropriate AAAA record may also be configured.
46. DNS Verification
From your computer:
nslookup learn.cresignsys.com
or:
nslookup learn.cresignsys.com 8.8.8.8
You should see your server IP.
47. Important
Do not confuse:
DNS points correctly
with:
Nginx configured correctly
Both are required.
48. Troubleshooting DNS
If:
nslookup
returns:
wrong IP
the problem is DNS.
If it returns the correct IP but the site doesn’t work:
DNS ✓
then investigate:
network
Nginx
PHP-FPM
WordPress
49. Step 22 — HTTP Test
Before SSL:
curl -I http://learn.cresignsys.com
Expected:
HTTP/1.1 200 OK
or possibly a redirect depending on your configuration.
50. Step 23 — SSL
Once HTTP works, configure HTTPS.
For a typical public website, Let’s Encrypt is a common certificate source.
Your final flow becomes:
https://learn.cresignsys.com
↓
443
↓
Nginx
↓
PHP-FPM
↓
WordPress
51. Why Test HTTP First?
If you configure everything simultaneously:
DNS
+
Nginx
+
PHP
+
WordPress
+
SSL
and the site fails, troubleshooting becomes harder.
Instead:
DNS
↓
HTTP
↓
Nginx
↓
PHP
↓
WordPress
↓
SSL
Test one layer at a time.
52. Certificate Validation
After SSL installation:
curl -I https://learn.cresignsys.com
You want a successful HTTPS response.
For example:
HTTP/2 200
or a legitimate redirect.
53. Test Certificate
You can inspect TLS with:
openssl s_client \
-connect learn.cresignsys.com:443 \
-servername learn.cresignsys.com
This is useful for advanced certificate troubleshooting.
54. HTTP → HTTPS Redirect
A common final configuration is:
http://learn.cresignsys.com
↓
301
↓
https://learn.cresignsys.com
Then:
HTTPS
↓
Nginx
↓
WordPress
55. Why 301?
A permanent redirect communicates that the preferred URL has moved permanently.
Example:
HTTP
↓
301
↓
HTTPS
56. Final Website Test
Test:
curl -I https://learn.cresignsys.com
Then:
curl -IL http://learn.cresignsys.com
You want to understand every response.
For example:
HTTP
↓
301
↓
HTTPS
↓
200
57. Test WordPress
Run:
wp core is-installed
Then:
wp db check
Then:
wp option get home
Then:
wp option get siteurl
58. Test PHP-FPM
Check:
sudo systemctl status php8.3-fpm
Then:
ls -l /run/php/
Confirm the expected socket exists.
59. Test Nginx
Run:
sudo nginx -t
Then:
sudo systemctl status nginx
60. Test MySQL
Run:
sudo systemctl status mysql
Then:
wp db check
This tests the application-to-database path.
61. Complete Health Check
Your provisioning system can eventually run:
DNS ✓
↓
Nginx ✓
↓
PHP-FPM ✓
↓
PHP socket ✓
↓
WordPress ✓
↓
Database ✓
↓
HTTPS ✓
Only then mark the site:
ACTIVE
62. Site Status
Your hosting database could maintain:
PENDING
PROVISIONING
ACTIVE
SUSPENDED
ERROR
DELETING
For example:
learn.cresignsys.com
STATUS = ACTIVE
63. What Happens If Provisioning Fails?
Suppose:
1. Linux user ✓
2. Directory ✓
3. Database ✓
4. PHP-FPM ✓
5. Nginx ✗
Your automation should not report:
Website created successfully
Instead:
PROVISIONING FAILED
Stage: Nginx configuration
Reason: nginx -t failed
64. Atomic Provisioning
A professional provisioning system should be designed so that partial failures are handled safely.
Conceptually:
Create
↓
Validate
↓
Continue
↓
Validate
↓
Continue
rather than:
run 20 commands
↓
hope everything worked
65. Example Provisioning State
CREATE USER
↓
SUCCESS
↓
CREATE DIRECTORY
↓
SUCCESS
↓
CREATE DATABASE
↓
SUCCESS
↓
CREATE PHP-FPM
↓
SUCCESS
↓
CREATE NGINX
↓
FAIL
The system should record:
site status = ERROR
failed stage = NGINX
and preserve enough information to repair or roll back safely.
66. Rollback
If a provisioning operation fails, you may need to remove resources that were already created.
For example:
database
user
directory
PHP pool
Nginx config
But rollback should be deliberate.
Never automatically delete customer data without carefully defining the lifecycle rules.
67. Provisioning Log
For each site, keep a log such as:
2026-08-13 15:10 CREATE USER
2026-08-13 15:10 CREATE DIRECTORY
2026-08-13 15:11 CREATE DATABASE
2026-08-13 15:11 CREATE PHP POOL
2026-08-13 15:11 CREATE NGINX CONFIG
2026-08-13 15:11 NGINX TEST
2026-08-13 15:12 WORDPRESS INSTALL
2026-08-13 15:12 SSL INSTALL
2026-08-13 15:13 HEALTH CHECK
2026-08-13 15:13 ACTIVE
This will become very valuable when you have many websites.
68. Your Hosting Platform Becomes a State Machine
Conceptually:
NEW
↓
PROVISIONING
↓
CONFIGURED
↓
TESTING
↓
ACTIVE
Failure:
PROVISIONING
↓
ERROR
Suspension:
ACTIVE
↓
SUSPENDED
Deletion:
SUSPENDED
↓
DELETING
↓
DELETED
This is much more reliable than simply checking whether a directory exists.
69. Manual vs Automated
Manual
20 commands
×
100 websites
=
2000+ operations
Automated
hosting-create domain.com
and the system performs the defined workflow.
This is why automation is essential for your CresignSys hosting platform.
70. Your Final Provisioning Architecture
HOSTING PANEL
│
▼
SITE CREATION
│
▼
PROVISIONING ENGINE
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
Linux Nginx MySQL
User Config DB
│ │ │
▼ ▼ ▼
Website Files PHP-FPM DB User
│
▼
WordPress
│
▼
SSL
│
▼
HEALTH CHECK
│
▼
ACTIVE
71. The Complete Command-Level Workflow
The actual automation eventually resembles:
1. validate_domain
2. generate_site_id
3. create_linux_user
4. create_directories
5. set_permissions
6. create_database
7. create_database_user
8. grant_database_privileges
9. create_php_fpm_pool
10. validate_php_fpm
11. create_nginx_config
12. validate_nginx
13. reload_nginx
14. download_wordpress
15. create_wp_config
16. install_wordpress
17. configure_dns / verify_dns
18. install_ssl
19. test_https
20. run_health_check
21. mark_site_active
72. Critical Rule
Don’t make the provisioning script dependent on assumptions such as:
PHP = 8.3
socket = /run/php/php8.3-fpm.sock
Instead, your hosting platform should detect or centrally define:
PHP version
PHP-FPM service
socket location
Nginx paths
MySQL version
website storage path
This makes the platform maintainable.
73. Configuration Template System
Your previous CresignSys work with Nginx templates now becomes very useful.
Instead of generating arbitrary configuration:
template
+
site variables
=
site configuration
For example:
DOMAIN
WEB_ROOT
PHP_SOCKET
LOG_PATH
are inserted into a template.
74. Example Template Variables
{{DOMAIN}}
{{WEB_ROOT}}
{{PHP_SOCKET}}
{{SITE_USER}}
{{SITE_ID}}
Then:
learn.cresignsys.com
generates:
server_name learn.cresignsys.com;
root /storage/websites/learn.cresignsys.com/public;
fastcgi_pass unix:/run/php/learn.sock;
75. Why Templates Are Better
Without templates:
manual configuration
With templates:
standardized configuration
Benefits:
consistency
repeatability
fewer mistakes
easy updates
automation
76. The Golden Rule of Provisioning
Every site should be created from the same validated architecture.
For example:
Site A
=
standard template
Site B
=
standard template
Site C
=
standard template
Only the site-specific variables change.
77. Lesson 063 — Core Principle
You now understand the complete journey:
DOMAIN
│
▼
DNS
│
▼
SERVER IP
│
▼
NGINX
│
▼
PHP-FPM POOL
│
▼
WORDPRESS
│
▼
MYSQL
│
▼
HTTPS / SSL
│
▼
HEALTH CHECK
│
▼
LIVE
And your future command:
sudo hosting-create example.com
is essentially an automation engine that creates and validates every layer above.
Next Lesson — 064
Build the hosting-create Provisioning Engine
We will now stop discussing the architecture only conceptually and design the actual CresignSys Hosting Platform provisioning script.
It will have a structure like:
/usr/local/bin/
└── hosting-create
/etc/cresignsys/
├── hosting.conf
├── nginx-templates/
├── php-templates/
└── site-defaults/
/storage/websites/
├── site1.com/
├── site2.com/
└── site3.com/
The provisioning engine will accept:
sudo hosting-create example.com
and systematically create:
Linux user
→ directories
→ permissions
→ MySQL database
→ database user
→ PHP-FPM pool
→ Nginx config
→ WordPress
→ SSL
→ health check
with validation at every stage instead of blindly executing commands.
Leave a Reply