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.
Leave a Reply