CresignSys Learn — Lesson 067

Written by

in

Build hosting-list, hosting-info & hosting-health

A hosting platform needs two capabilities:

PROVISION
   ↓
OBSERVE
   ↓
DIAGNOSE
   ↓
REPAIR

We already started the provisioning engine.

Now we build the observability layer.


1. Three New Commands

We want:

sudo hosting-list
sudo hosting-info example.com
sudo hosting-health example.com

They have different purposes.


2. hosting-list

Purpose:

Show all websites managed by CresignSys.

Example:

DOMAIN                         STATUS
------------------------------------------------------
learn.cresignsys.com           ACTIVE
shop.cresignsys.com            ACTIVE
medical.cresignsys.com         ACTIVE
example.com                    ERROR

3. hosting-info

Purpose:

Show the configuration and metadata of one website.

Example:

Domain:
example.com

Site ID:
1027

Status:
ACTIVE

Linux User:
csp1027

Web Root:
/storage/websites/example.com/public

PHP Version:
8.3

Database:
csp1027_wp

4. hosting-health

Purpose:

Test whether the website is actually functioning.

Example:

Filesystem       ✓
PHP-FPM          ✓
PHP Socket       ✓
Nginx            ✓
Database         ✓
WordPress        ✓
HTTP             ✓
HTTPS            ✓

5. Why Separate Them?

Don’t make:

hosting-info

perform repairs.

Don’t make:

hosting-health

modify configuration.

Observability commands should ideally be:

READ ONLY

This is an important operational principle.


6. Source of Truth

We previously created:

/var/lib/cresignsys/sites/

For example:

/var/lib/cresignsys/sites/
├── site1.example.com/
├── site2.example.com/
└── learn.cresignsys.com/

Each site can contain:

state
site.conf
provision.log

7. site.conf

Example:

DOMAIN=learn.cresignsys.com
SITE_ID=1027
SITE_USER=csp1027
WEB_ROOT=/storage/websites/learn.cresignsys.com/public
DB_NAME=csp1027_wp
DB_USER=csp1027_db
PHP_VERSION=8.3
PHP_SOCKET=/run/php/csp1027.sock
STATUS=ACTIVE

Notice:

Do not store the database password here.


8. Build hosting-list

Create:

sudo nano /usr/local/bin/hosting-list

Start:

#!/usr/bin/env bash

set -Eeuo pipefail

STATE_DIR="/var/lib/cresignsys/sites"

if [[ ! -d "$STATE_DIR" ]]; then
    echo "No CresignSys sites found."
    exit 0
fi

9. Print Header

printf "%-35s %-12s\n" "DOMAIN" "STATUS"
printf "%-35s %-12s\n" "-----------------------------------" "------------"

10. Read Site Directories

for site_dir in "$STATE_DIR"/*; do

    [[ -d "$site_dir" ]] || continue

    DOMAIN="$(basename "$site_dir")"
    STATE_FILE="${site_dir}/state"

    STATUS="UNKNOWN"

    if [[ -f "$STATE_FILE" ]]; then
        STATUS="$(
            awk -F= '$1=="STATUS" {print $2}' "$STATE_FILE"
        )"
    fi

    printf "%-35s %-12s\n" "$DOMAIN" "$STATUS"
done

11. Make It Executable

sudo chmod 755 /usr/local/bin/hosting-list

Run:

sudo hosting-list

12. First Improvement — Sort the List

Instead of relying on filesystem ordering:

for site_dir in "$STATE_DIR"/*;

you can collect and sort the output.

This becomes useful once you have dozens or hundreds of websites.


13. Better Output

Eventually:

DOMAIN                         STATUS
------------------------------------------------------
acme.com                      ACTIVE
learn.cresignsys.com          ACTIVE
shop.cresignsys.com           ACTIVE
test.example.com              ERROR

Alphabetical ordering makes administration easier.


14. Build hosting-info

Create:

sudo nano /usr/local/bin/hosting-info

Start:

#!/usr/bin/env bash

set -Eeuo pipefail

STATE_DIR="/var/lib/cresignsys/sites"

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

DOMAIN="$1"
SITE_DIR="${STATE_DIR}/${DOMAIN}"
SITE_CONF="${SITE_DIR}/site.conf"

15. Verify Site Exists

if [[ ! -f "$SITE_CONF" ]]; then
    echo "ERROR: Site not found: $DOMAIN"
    exit 1
fi

16. Load Configuration

source "$SITE_CONF"

Now variables become available:

DOMAIN
SITE_ID
SITE_USER
WEB_ROOT
DB_NAME
DB_USER
PHP_VERSION
PHP_SOCKET
STATUS

17. Display Site Information

echo
echo "CresignSys Site Information"
echo "============================"
echo
echo "Domain       : $DOMAIN"
echo "Site ID      : $SITE_ID"
echo "Status       : $STATUS"
echo "Linux User   : $SITE_USER"
echo "Web Root     : $WEB_ROOT"
echo "PHP Version  : $PHP_VERSION"
echo "PHP Socket   : $PHP_SOCKET"
echo "Database     : $DB_NAME"
echo "DB User      : $DB_USER"
echo

18. Show Directory Status

Add:

if [[ -d "$WEB_ROOT" ]]; then
    echo "Web Root     : EXISTS"
else
    echo "Web Root     : MISSING"
fi

19. Show Disk Usage

Use:

du -sh "$(dirname "$WEB_ROOT")" 2>/dev/null || true

Example:

Disk Usage   : 1.2G

20. Don’t Use du as a Quota System

This is important.

du tells you approximately:

how much space is currently used

It does not enforce:

maximum storage

Quota enforcement is a separate feature.


21. Show PHP-FPM Status

Use the configured service:

systemctl is-active "php${PHP_VERSION}-fpm" \
    2>/dev/null || true

Output:

PHP-FPM      : active

22. Check PHP Socket

if [[ -S "$PHP_SOCKET" ]]; then
    echo "PHP Socket   : EXISTS"
else
    echo "PHP Socket   : MISSING"
fi

This is a very useful diagnostic.


23. Check Nginx Configuration

NGINX_CONF="/etc/nginx/sites-available/${DOMAIN}.conf"

if [[ -f "$NGINX_CONF" ]]; then
    echo "Nginx Config : EXISTS"
else
    echo "Nginx Config : MISSING"
fi

24. Check Enabled Site

NGINX_LINK="/etc/nginx/sites-enabled/${DOMAIN}.conf"

if [[ -L "$NGINX_LINK" ]]; then
    echo "Nginx Site   : ENABLED"
else
    echo "Nginx Site   : NOT ENABLED"
fi

25. Check WordPress

if [[ -f "$WEB_ROOT/wp-config.php" ]]; then
    echo "WordPress    : CONFIGURED"
else
    echo "WordPress    : NOT CONFIGURED"
fi

26. Don’t Run wp as Root

If you need deeper WordPress information:

sudo -u "$SITE_USER" \
wp --path="$WEB_ROOT" core is-installed

The principle remains:

WordPress operations
        ↓
site user

not:

root

27. Build hosting-health

Now the most useful command.

Create:

sudo nano /usr/local/bin/hosting-health

Start:

#!/usr/bin/env bash

set -Eeuo pipefail

STATE_DIR="/var/lib/cresignsys/sites"

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

DOMAIN="$1"
SITE_DIR="${STATE_DIR}/${DOMAIN}"
SITE_CONF="${SITE_DIR}/site.conf"

28. Load Site Information

if [[ ! -f "$SITE_CONF" ]]; then
    echo "ERROR: Site not found."
    exit 1
fi

source "$SITE_CONF"

29. Health Check Philosophy

Each test should return:

PASS

or:

FAIL

Do not let one failed check hide the other checks.


30. Helper Function

Create:

check() {
    local name="$1"
    shift

    if "$@" >/dev/null 2>&1; then
        printf "[OK]   %-20s\n" "$name"
        return 0
    else
        printf "[FAIL] %-20s\n" "$name"
        return 1
    fi
}

Now:

check "Web Root" test -d "$WEB_ROOT"

prints:

[OK] Web Root

31. Don’t Stop After One Failure

For health checking, we want:

Filesystem    ✓
PHP-FPM       ✓
Socket        ✗
Nginx         ✓
Database      ✓
WordPress     ✓

rather than stopping at:

Socket failed

We want to know everything that is wrong.


32. Maintain Health Status

HEALTH_OK=1

If a test fails:

HEALTH_OK=0

At the end:

if [[ "$HEALTH_OK" -eq 1 ]]; then
    echo
    echo "Overall Status: HEALTHY"
    exit 0
else
    echo
    echo "Overall Status: UNHEALTHY"
    exit 1
fi

33. Filesystem Check

if ! check "Web Root" test -d "$WEB_ROOT"; then
    HEALTH_OK=0
fi

34. WordPress Check

if ! check "WordPress" \
    sudo -u "$SITE_USER" \
    wp --path="$WEB_ROOT" core is-installed
then
    HEALTH_OK=0
fi

35. Database Check

if ! check "Database" \
    sudo -u "$SITE_USER" \
    wp --path="$WEB_ROOT" db check
then
    HEALTH_OK=0
fi

This tests the actual WordPress database connection.

That is better than merely checking whether MySQL is running.


36. PHP-FPM Service Check

PHP_SERVICE="php${PHP_VERSION}-fpm"

if ! check "PHP-FPM" \
    systemctl is-active "$PHP_SERVICE"
then
    HEALTH_OK=0
fi

37. PHP Socket Check

if ! check "PHP Socket" test -S "$PHP_SOCKET"; then
    HEALTH_OK=0
fi

38. Nginx Check

Global Nginx:

if ! check "Nginx Service" \
    systemctl is-active nginx
then
    HEALTH_OK=0
fi

39. Nginx Configuration Check

if ! check "Nginx Config" nginx -t; then
    HEALTH_OK=0
fi

40. Domain HTTP Check

Now test the actual website:

if ! check "HTTP" \
    curl -fsSI --max-time 10 "http://${DOMAIN}"
then
    HEALTH_OK=0
fi

This is important because:

Nginx service running

does not necessarily mean:

your domain works

41. HTTP Health Check

The path is now:

Browser
 ↓
DNS
 ↓
Server
 ↓
Nginx
 ↓
Domain server block
 ↓
WordPress

This tests multiple layers simultaneously.


42. HTTPS Check

If SSL is installed:

if ! check "HTTPS" \
    curl -fsSI --max-time 10 "https://${DOMAIN}"
then
    HEALTH_OK=0
fi

But if SSL hasn’t been configured yet, don’t mark HTTPS as a required failure.


43. SSL-Aware Health Check

You can detect whether HTTPS is expected.

For example:

SSL_STATUS=ACTIVE

in site.conf.

Then:

SSL ACTIVE
    ↓
test HTTPS

SSL NOT ACTIVE
    ↓
skip HTTPS test

44. Health Output

A good final result:

CresignSys Health Check
========================

Domain          : learn.cresignsys.com

[OK]   Web Root
[OK]   PHP-FPM
[OK]   PHP Socket
[OK]   Nginx Service
[OK]   Nginx Config
[OK]   WordPress
[OK]   Database
[OK]   HTTP
[OK]   HTTPS

Overall Status: HEALTHY

45. Failed Example

CresignSys Health Check
========================

Domain          : learn.cresignsys.com

[OK]   Web Root
[OK]   PHP-FPM
[FAIL] PHP Socket
[OK]   Nginx Service
[OK]   Nginx Config
[OK]   WordPress
[OK]   Database
[FAIL] HTTP

Overall Status: UNHEALTHY

This immediately tells you where to investigate.


46. Why Socket Failure Can Cause HTTP Failure

The chain:

Nginx
 ↓
PHP-FPM socket
 ↓
PHP

If the socket is missing:

Nginx
 ↓
socket ✗

dynamic WordPress requests can return:

502 Bad Gateway

47. Health Checks Are Diagnostic Clues

For example:

Nginx ✓

Socket ✗

Likely area:

PHP-FPM pool

Socket ✓

HTTP ✗

Investigate:

Nginx routing
DNS
firewall
domain configuration

HTTP ✓

WordPress ✗

Investigate:

database
filesystem
WordPress installation

48. HTTP Status Codes

Your health checker can eventually inspect HTTP status.

For example:

200 → healthy
301/302 → redirect
403 → access problem
404 → routing/resource problem
500 → application error
502 → PHP-FPM problem
503 → service unavailable

This makes the health system more intelligent.


49. Capture HTTP Status

Instead of:

curl -fsSI

you can eventually capture:

curl -o /dev/null -s -w "%{http_code}"

Then:

HTTP_STATUS=200

and your health engine can interpret it.


50. Don’t Treat Every Redirect as Failure

A website may legitimately return:

301

because:

HTTP → HTTPS

So:

301

is not necessarily unhealthy.

Your health system should understand expected behavior.


51. Add SSL Status

In your site.conf:

SSL_STATUS=ACTIVE

Then:

echo "SSL Status : $SSL_STATUS"

52. hosting-info Should Be Read Only

This command should never do:

systemctl restart nginx

or:

mysql ...

to modify anything.

Its role is:

inspect

53. hosting-health Should Also Be Read Only

It can run:

wp db check
nginx -t
curl
systemctl is-active

but should not:

restart services
change permissions
recreate databases

That belongs to:

hosting-repair

54. This Creates a Clean Operational Model

hosting-create
     ↓
CREATE

hosting-list
     ↓
OBSERVE

hosting-info
     ↓
INSPECT

hosting-health
     ↓
DIAGNOSE

hosting-repair
     ↓
FIX

This separation is extremely useful.


55. Add hosting-list --json

Eventually your control panel will need machine-readable output.

Human output:

DOMAIN STATUS

Machine output:

{
  "domain": "example.com",
  "status": "ACTIVE"
}

This allows the web panel to consume the CLI safely.


56. But Don’t Parse Human Text

Avoid designing the control panel to parse:

DOMAIN       STATUS
example.com  ACTIVE

Human formatting can change.

Instead:

hosting-list --json

should produce structured JSON.


57. JSON Becomes the API Boundary

Eventually:

CHP Web Panel
       ↓
API
       ↓
Provisioning Engine
       ↓
JSON result

For example:

{
  "domain": "example.com",
  "status": "ACTIVE",
  "php": "8.3",
  "database": "healthy",
  "http": 200
}

58. This Is Better Than Shell Scraping

Bad architecture:

Web Panel
 ↓
grep output from Bash

Better:

Web Panel
 ↓
structured API
 ↓
provisioning engine

We will eventually move toward the second architecture.


59. Add Timestamps

Health results should eventually contain:

checked_at

For example:

2026-08-13 15:45:22

This matters because:

healthy now

does not mean:

healthy forever

60. Health History

Eventually store:

site_id
timestamp
http_status
php_status
database_status
ssl_status
overall_status

Then the panel can show:

Health
──────────────
15:00 ✓
15:05 ✓
15:10 ✓
15:15 ✗
15:20 ✓

61. This Enables Monitoring

Once health checks exist, you can later build:

automatic monitoring

such as:

if website unhealthy
        ↓
alert administrator

This is where the observability layer becomes operational monitoring.


62. Disk Usage in hosting-health

Add:

du -sh "$(dirname "$WEB_ROOT")"

Then:

Disk Usage : 1.4G

Eventually compare against the hosting plan.


63. Storage Warning

Suppose the plan allows:

5 GB

and usage is:

4.8 GB

The system can report:

WARNING: 96% storage used

This is the beginning of resource monitoring.


64. Database Size

You can also measure database size.

Conceptually:

SELECT
    ROUND(SUM(data_length + index_length) / 1024 / 1024, 2)
FROM information_schema.tables
WHERE table_schema = 'database_name';

Then show:

Database Size : 420 MB

65. Don’t Query Every Table Excessively

For a small number of websites, this is fine.

For hundreds or thousands:

health check
 ↓
hundreds of SQL queries

can itself become a performance problem.

Later you will want:

cached metrics

and periodic collection.


66. Website Health vs Server Health

These are different.

Server health

CPU
RAM
Disk
MySQL
Nginx
PHP-FPM

Website health

DNS
HTTP
WordPress
Database
SSL
Filesystem

You need both.


67. Example

Server:

CPU ✓
RAM ✓
Disk ✓
Nginx ✓
MySQL ✓

But:

example.com → HTTP 502

Therefore:

server healthy
site unhealthy

This distinction prevents misleading diagnostics.


68. Site Health Model

Eventually:

SITE HEALTH
│
├── DNS
├── HTTP
├── HTTPS
├── Nginx
├── PHP-FPM
├── PHP socket
├── WordPress
├── Database
├── Filesystem
└── SSL

69. Server Health Model

SERVER HEALTH
│
├── CPU
├── Memory
├── Storage
├── Inodes
├── Nginx
├── PHP-FPM
├── MySQL
└── Network

70. Control Panel Dashboard

These commands now provide the foundation for a dashboard:

                    CHP DASHBOARD

Sites
────────────────────────────────────
Total: 42

Active: 40
Error: 2

Server
────────────────────────────────────
CPU:      Healthy
Memory:   Healthy
Storage:  62%
MySQL:    Healthy
Nginx:    Healthy

Websites
────────────────────────────────────
learn.cresignsys.com       ✓
shop.cresignsys.com        ✓
example.com                ✗

71. The Architecture Is Growing

We now have:

              CHP
               │
     ┌─────────┴─────────┐
     ▼                   ▼
Provisioning         Observability
     │                   │
hosting-create      hosting-list
                    hosting-info
                    hosting-health

Next:

Repair

Then:

SSL

Then:

Backup

72. Lesson 067 — Core Principle

A professional hosting platform must be able to answer:

What websites exist, how are they configured, and are they actually working?

That requires three separate capabilities:

hosting-list
     ↓
What exists?

hosting-info
     ↓
How is it configured?

hosting-health
     ↓
Is it working?

This is the foundation of reliable hosting operations.


Next Lesson — 068

Build hosting-repair

We now have:

CREATE
  ↓
LIST
  ↓
INFO
  ↓
HEALTH

The next logical component is:

HEALTH
  ↓
FAILURE
  ↓
REPAIR

We will build:

sudo hosting-repair example.com

with targeted repair operations such as:

sudo hosting-repair example.com --php
sudo hosting-repair example.com --nginx
sudo hosting-repair example.com --permissions
sudo hosting-repair example.com --wordpress

without deleting the website or database.

Comments

Leave a Reply

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