CresignSys Learn — Lesson 064

Written by

in

Build the hosting-create Provisioning Engine

We now move from learning individual server components to building the actual automation layer for your CresignSys Hosting Platform.

The goal is:

sudo hosting-create example.com

and the server automatically performs the required provisioning steps.


1. What Are We Building?

We want a command:

hosting-create DOMAIN

For example:

sudo hosting-create learn.cresignsys.com

The system should create:

Domain
 ↓
Site ID
 ↓
Linux user
 ↓
Website directories
 ↓
Permissions
 ↓
MySQL database
 ↓
MySQL user
 ↓
PHP-FPM pool
 ↓
Nginx configuration
 ↓
WordPress
 ↓
SSL
 ↓
Health check

2. Why Build Our Own Tool?

Instead of manually doing:

mkdir
useradd
chown
mysql
wp
nginx
certbot
systemctl

for every website, create one controlled interface:

sudo hosting-create domain.com

This becomes the foundation of your own:

CresignSys Hosting Platform — CHP


3. High-Level Architecture

                     HOSTING-CREATE
                           │
             ┌─────────────┼─────────────┐
             │             │             │
             ▼             ▼             ▼
          Linux          MySQL          PHP-FPM
             │             │             │
             └─────────────┼─────────────┘
                           │
                           ▼
                         Nginx
                           │
                           ▼
                       WordPress
                           │
                           ▼
                          SSL

4. Directory Structure

We should keep the hosting platform organized.

/etc/cresignsys/
│
├── hosting.conf
│
├── nginx-templates/
│   └── wordpress.conf
│
├── php-templates/
│   └── wordpress-pool.conf
│
└── scripts/
    ├── common.sh
    ├── mysql.sh
    ├── nginx.sh
    ├── php-fpm.sh
    └── wordpress.sh

Command:

/usr/local/bin/hosting-create

5. Website Storage

Your websites can remain under:

/storage/websites/

For example:

/storage/websites/
│
├── learn.cresignsys.com/
│   └── public/
│
├── shop.cresignsys.com/
│   └── public/
│
└── example.com/
    └── public/

6. Central Configuration

Create:

/etc/cresignsys/hosting.conf

Conceptually:

WEB_ROOT_BASE="/storage/websites"
NGINX_AVAILABLE="/etc/nginx/sites-available"
NGINX_ENABLED="/etc/nginx/sites-enabled"
PHP_SOCKET_BASE="/run/php"

Now the script doesn’t need these paths hard-coded everywhere.


7. Why Central Configuration?

Suppose later you change:

/storage/websites

to:

/storage/hosting

You should ideally change:

hosting.conf

rather than editing ten different scripts.


8. Script Entry Point

Create:

/usr/local/bin/hosting-create

The first line:

#!/usr/bin/env bash

This tells Linux to execute the script using Bash.


9. Strict Bash Mode

A good starting point is:

set -Eeuo pipefail

This provides stronger error handling.

Conceptually:

-e
→ stop when a command fails

-u
→ detect unset variables

-E
→ preserve ERR traps

pipefail
→ detect failures inside pipelines

10. Require Root

Website provisioning needs privileged operations.

Therefore:

if [[ $EUID -ne 0 ]]; then
    echo "Run as root."
    exit 1
fi

Then:

sudo hosting-create example.com

works because sudo executes the script as root.


11. Validate Arguments

The command requires a domain:

hosting-create example.com

If somebody runs:

hosting-create

the script should stop.

Conceptually:

if [[ $# -ne 1 ]]; then
    echo "Usage: hosting-create DOMAIN"
    exit 1
fi

12. Store the Domain

DOMAIN="$1"

Now:

DOMAIN
=
example.com

13. Domain Validation

Never directly use arbitrary user input in:

Linux usernames
SQL identifiers
file paths
Nginx configuration

First validate and normalize it.

For example:

example.com

is acceptable.

But:

../../something

must never become a filesystem path.


14. Generate Site ID

The domain:

learn.cresignsys.com

could become:

learn_cresignsys_com

Conceptually:

SITE_ID="${DOMAIN//./_}"

But a production implementation should also remove/reject unsafe characters and enforce length/uniqueness rules.


15. Don’t Trust the Site ID Blindly

A safe provisioning system should check:

allowed characters
maximum length
reserved names
existing users
existing directories
existing databases
existing Nginx configuration

before creating anything.


16. Detect Existing Website

Before provisioning:

Does directory exist?
Does Linux user exist?
Does database exist?
Does PHP pool exist?
Does Nginx config exist?

If the site already exists, don’t blindly overwrite it.


17. Idempotency

A very important automation concept:

Idempotency

A provisioning command should ideally be safe to run again or should detect that provisioning has already occurred.

For example:

hosting-create example.com

first run:

CREATE

second run:

ALREADY EXISTS

rather than destroying the website.


18. Provisioning State

Create a state directory:

/var/lib/cresignsys/sites/

For example:

/var/lib/cresignsys/sites/example.com/

Store metadata such as:

status
site_id
created_at
php_version
database_name

19. Site Metadata

A conceptual record:

DOMAIN=learn.cresignsys.com
SITE_ID=learn_cresignsys_com
USER=learn_cresignsys_com
DATABASE=learn_cresignsys_com_db
DB_USER=learn_cresignsys_com_dbuser
PHP_SOCKET=/run/php/learn_cresignsys_com.sock
WEB_ROOT=/storage/websites/learn.cresignsys.com/public

This gives every component a common identity.


20. Step 1 — Create Linux User

The provisioning engine checks:

Does user exist?

If not:

create user

If it already exists:

don't recreate it

This is safer than:

useradd ...

every time.


21. Step 2 — Create Directories

Create:

/storage/websites/example.com/

with:

public/
logs/
private/

For example:

/storage/websites/example.com/
├── public/
├── logs/
└── private/

22. Why Separate Directories?

public

Web-accessible files.

logs

Site-related logs if your architecture uses them.

private

Files that must not be directly accessible from HTTP.


23. Step 3 — Set Ownership

The site user should own its website files according to your chosen security model.

For example:

example.com/
owner = example_user
group = example_user

24. Step 4 — Create Database

The script generates:

DATABASE_NAME
DATABASE_USER
DATABASE_PASSWORD

For example:

example_db
example_dbuser
[random password]

The password should be generated securely.


25. Generate Secrets

Don’t use:

password123

or:

example.com123

Use a cryptographically secure random generator.

For example, Linux provides:

openssl rand -base64 32

or other secure methods.

The exact secret-management strategy becomes especially important when building a control panel.


26. Database Password Storage

Do not casually store database passwords in:

public/

or:

Nginx configuration

The password belongs in the site’s protected configuration.

WordPress itself needs the credential, so the file containing it must have appropriate permissions.


27. Step 5 — PHP-FPM Pool

Generate:

/etc/php/<version>/fpm/pool.d/example.conf

from a template.

The template receives:

SITE_ID
USER
GROUP
SOCKET

28. Template Concept

Instead of manually generating:

[example]
user = example_user
group = example_user
listen = /run/php/example.sock

create a reusable template:

[{{SITE_ID}}]

user = {{SITE_USER}}
group = {{SITE_USER}}

listen = {{PHP_SOCKET}}

Then replace the variables.


29. Why Templates?

You can have:

100 websites

but only:

1 standard PHP-FPM template

This dramatically reduces configuration inconsistencies.


30. Validate PHP-FPM

After creating a pool:

PHP-FPM configuration
        ↓
validate
        ↓
reload PHP-FPM

Don’t reload first and discover later that the configuration is invalid.


31. Step 6 — Nginx Template

Use:

/etc/cresignsys/nginx-templates/wordpress.conf

Variables:

DOMAIN
WEB_ROOT
PHP_SOCKET

Generated configuration:

/etc/nginx/sites-available/example.com

32. Enable Nginx Site

Create the symlink:

sites-available/example.com
          ↓
sites-enabled/example.com

Then:

nginx -t

If successful:

systemctl reload nginx

33. Never Skip nginx -t

Your automation should follow:

write config
 ↓
nginx -t
 ↓
success?
 ├── NO → stop
 └── YES
       ↓
    reload

This prevents a bad site configuration from being blindly activated.


34. Step 7 — WordPress

Once:

Nginx ✓
PHP-FPM ✓
MySQL ✓

the script downloads WordPress.

Use WP-CLI where possible.


35. Why WP-CLI Is Ideal

Your hosting system can execute:

wp core download
wp config create
wp core install
wp option update
wp plugin install

This makes WordPress provisioning highly automatable.


36. WordPress Configuration

The script supplies:

DB_NAME
DB_USER
DB_PASSWORD
DB_HOST

and creates:

wp-config.php

37. WordPress Installation

Then supply:

URL
title
administrator username
administrator password
administrator email

The site becomes operational.


38. Step 8 — SSL

SSL should be installed only after:

DNS ✓
HTTP ✓
Nginx ✓

Otherwise certificate validation can fail.


39. SSL Workflow

DNS
 ↓
HTTP website
 ↓
Certificate request
 ↓
Certificate issued
 ↓
Nginx HTTPS configuration
 ↓
HTTP → HTTPS redirect

40. Step 9 — Health Check

Your provisioning engine should not simply assume success.

Check:

Nginx running?
PHP-FPM running?
MySQL running?
socket exists?
database accessible?
WordPress installed?
HTTPS accessible?

41. Health Check Example

Conceptually:

[✓] Linux user
[✓] Website directory
[✓] Database
[✓] Database user
[✓] PHP-FPM pool
[✓] PHP socket
[✓] Nginx configuration
[✓] WordPress
[✓] SSL
[✓] HTTPS

Then:

SITE STATUS: ACTIVE

42. Failure Handling

Suppose:

[✓] Linux user
[✓] Directory
[✓] Database
[✓] PHP-FPM
[✗] Nginx

The script should produce something like:

Provisioning failed.
Stage: Nginx
Reason: configuration test failed.

Not:

Website created successfully.

43. Logging

Create:

/var/log/cresignsys/

Then:

/var/log/cresignsys/hosting-create.log

The provisioning engine can record:

timestamp
domain
stage
success/failure
error

44. Example Log

2026-08-13 15:20 example.com CREATE_USER SUCCESS
2026-08-13 15:20 example.com CREATE_DIRECTORY SUCCESS
2026-08-13 15:20 example.com CREATE_DATABASE SUCCESS
2026-08-13 15:21 example.com CREATE_PHP_POOL SUCCESS
2026-08-13 15:21 example.com CREATE_NGINX SUCCESS
2026-08-13 15:21 example.com NGINX_TEST SUCCESS
2026-08-13 15:22 example.com WORDPRESS SUCCESS
2026-08-13 15:23 example.com SSL SUCCESS
2026-08-13 15:23 example.com HEALTH_CHECK SUCCESS

45. Recommended Script Structure

Rather than putting everything into one enormous Bash file:

hosting-create

can call helper functions/scripts.

For example:

hosting-create
│
├── validate
├── create-user
├── create-files
├── create-database
├── create-php
├── create-nginx
├── install-wordpress
├── install-ssl
└── health-check

This makes debugging easier.


46. Function-Based Design

Even if everything remains in one Bash script initially, divide it into functions:

validate_domain()
create_site_user()
create_directories()
create_database()
create_php_pool()
create_nginx_config()
install_wordpress()
install_ssl()
health_check()

Then the main flow becomes:

validate
 ↓
create user
 ↓
create directories
 ↓
create database
 ↓
create PHP
 ↓
create Nginx
 ↓
WordPress
 ↓
SSL
 ↓
health check

47. Validation Before Creation

A strong design validates:

domain syntax
available disk space
PHP version
MySQL availability
Nginx availability
WP-CLI availability
SSL tool availability
DNS status

before performing irreversible operations.


48. Disk Space Check

Before creating a website:

df -h /storage

If storage is almost full:

don't provision

Instead:

ERROR: insufficient storage

49. Memory Check

You can inspect:

free -h

This is important because creating many PHP-FPM pools can increase memory usage.


50. CPU Check

Check:

nproc

This tells you the number of available processing units.

It helps inform PHP-FPM sizing.


51. Never Automatically Choose Huge PHP Limits

Your script should not create every site with:

pm.max_children = 100

Instead, use a centrally defined profile.

For example:

Starter
Business
Professional

Each profile can have resource settings appropriate to the plan.


52. Hosting Plan Profiles

For example:

STARTER
PHP workers: low

BUSINESS
PHP workers: moderate

PROFESSIONAL
PHP workers: higher

These should be tuned from actual server capacity and workload rather than arbitrary marketing numbers.


53. This Connects to Your Hosting Plans

Your hosting platform can eventually store:

Plan
 ↓
Storage limit
 ↓
PHP worker limit
 ↓
Database limit
 ↓
Bandwidth policy
 ↓
Backup policy

Then provisioning reads the customer’s plan.


54. Example

Customer chooses:

Business

The platform creates:

5 GB storage
appropriate PHP-FPM limits
database
backup policy
SSL

This turns your infrastructure into an actual hosting product.


55. Site Database

Your control panel itself can maintain a database containing:

sites

Example fields:

id
domain
site_id
linux_user
document_root
database_name
database_user
php_version
plan_id
status
created_at

56. Why Have Two Database Layers?

You now have:

Hosting control database

Stores:

which websites exist

WordPress database

Stores:

what each WordPress website contains

They serve different purposes.


57. Example

Control database:

site_id = 27
domain = learn.cresignsys.com
plan = business
status = active

WordPress database:

wp_posts
wp_users
wp_options
...

58. This Is the Control Plane

Your hosting panel becomes the:

Control Plane

while the actual websites are:

Workloads

Conceptually:

                 CONTROL PLANE
                      │
          ┌───────────┼───────────┐
          ▼           ▼           ▼
        Site 1      Site 2      Site 3
        workload    workload    workload

59. Provisioning Is the Bridge

The control panel says:

Create site example.com

The provisioning engine translates that request into:

Linux
+
Nginx
+
PHP-FPM
+
MySQL
+
WordPress
+
SSL

60. The Long-Term CHP Architecture

                     CHP PANEL
                         │
                         ▼
                  SITE DATABASE
                         │
                         ▼
                PROVISIONING ENGINE
                         │
          ┌──────────────┼──────────────┐
          ▼              ▼              ▼
        Linux          Nginx          MySQL
          │              │              │
          └──────────────┼──────────────┘
                         ▼
                      PHP-FPM
                         │
                         ▼
                    WordPress
                         │
                         ▼
                        SSL

61. Important Security Rule

The web browser should never directly execute:

hosting-create

The control panel must authenticate and authorize the request first.

Eventually:

Admin
 ↓
CHP Panel
 ↓
authenticated API
 ↓
provisioning service
 ↓
root-level operations

62. Don’t Run the Web Application as Root

This is critical.

Do not make:

PHP
WordPress
control-panel web process

run as root merely because provisioning requires root.

Instead:

Web application
 ↓
restricted user
 ↓
controlled provisioning mechanism

63. Why?

If a WordPress vulnerability or panel vulnerability occurs and the application itself has root privileges:

application compromise
 ↓
root compromise
 ↓
entire server

The blast radius becomes enormous.


64. Separate Control Plane and Workload

Your architecture should eventually look like:

CHP Web Application
        │
        │ controlled request
        ▼
Provisioning Service
        │
        │ privileged operations
        ▼
Server

rather than:

WordPress/PHP
        │
        ▼
root shell

65. Lesson 064 — Core Principle

The provisioning engine is the automation bridge between your control panel and the Linux server.

The command:

sudo hosting-create example.com

is conceptually:

INPUT
  ↓
VALIDATE
  ↓
CREATE
  ↓
CONFIGURE
  ↓
TEST
  ↓
ACTIVATE

not simply:

INPUT
  ↓
RUN 20 COMMANDS

66. Final Architecture

                 CresignSys Hosting Platform
                           │
                           ▼
                     hosting-create
                           │
          ┌────────────────┼────────────────┐
          ▼                ▼                ▼
      Linux User        MySQL            PHP-FPM
          │                │                │
          ▼                ▼                ▼
       Website          Database          Socket
          │                │                │
          └────────────────┼────────────────┘
                           ▼
                         Nginx
                           │
                           ▼
                       WordPress
                           │
                           ▼
                          SSL
                           │
                           ▼
                     HEALTH CHECK
                           │
                           ▼
                         ACTIVE

Next Lesson — 065

Build the First Working hosting-create Script

The next lesson will be practical.

We will build the first version of:

sudo hosting-create example.com

using Bash, with:

  • domain validation
  • safe site-ID generation
  • Linux user creation
  • directory creation
  • ownership/permissions
  • MySQL database creation
  • database-user creation
  • PHP-FPM pool generation
  • Nginx template generation
  • configuration validation
  • WordPress installation
  • error handling
  • logging
  • final health check

The goal will be a real working first-generation CresignSys provisioning script, rather than only pseudocode.

Comments

Leave a Reply

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