CresignSys Learn — Lesson 063

Written by

in

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.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *