CresignSys Learn — Lesson 079

Written by

in

Migrate Existing Websites into the CHP Database

The next step is to bring your existing websites under CHP management without changing or breaking them.

Your existing structure is similar to:

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

The migration command will be:

sudo hosting-db-import example.com

The most important rule:

Import must be READ-ONLY first. It discovers the existing installation; it does not modify it.


1. Migration Architecture

                  EXISTING SERVER
                       │
                       ▼
                DISCOVERY ENGINE
                       │
          ┌────────────┼────────────┐
          ▼            ▼            ▼
       Files          Nginx       PHP-FPM
          │            │            │
          ▼            ▼            ▼
       WordPress     SSL          MySQL
          │            │            │
          └────────────┼────────────┘
                       ▼
                  CHP DATABASE
                       │
                       ▼
                    VERIFY
                       │
                       ▼
                 IMPORTED SITE

2. Safety Rule

hosting-db-import should initially perform:

READ
READ
READ
READ

and only write to:

/var/lib/cresignsys/chp.db

It should not:

modify Nginx
restart PHP
change permissions
change WordPress
change MySQL
issue SSL
delete files

3. Why Read-Only Discovery?

Suppose your existing site is already working:

example.com
   ↓
Nginx
   ↓
PHP-FPM
   ↓
WordPress

We don’t want the migration tool to suddenly regenerate:

Nginx

and introduce a problem.

Migration and provisioning are different operations.


4. Migration States

Add a site state:

DISCOVERED

Then:

DISCOVERED
    ↓
IMPORTED
    ↓
VERIFIED
    ↓
MANAGED

For a new CHP-created website:

PROVISIONING
    ↓
ACTIVE

5. Discovery vs Management

This distinction is important.

Discovered

CHP knows:

The website exists.

Imported

CHP has created database metadata.

Verified

CHP confirmed that the metadata matches the server.

Managed

CHP is authorized to modify the site configuration.

This avoids accidentally treating every discovered website as fully managed.


6. Add management_state

Update the sites table.

Add:

ALTER TABLE sites
ADD COLUMN management_state TEXT NOT NULL DEFAULT 'IMPORTED';

Better states:

DISCOVERED
IMPORTED
VERIFIED
MANAGED

For the initial migration, use:

IMPORTED

until verification succeeds.


7. Migration Command

Create:

sudo nano /usr/local/bin/hosting-db-import

Start with:

#!/usr/bin/env bash

set -Eeuo pipefail

8. Load CHP Configuration

source /etc/cresignsys/hosting.conf
source /etc/cresignsys/lib/common.sh
source /etc/cresignsys/lib/logging.sh
source /etc/cresignsys/lib/domain.sh
source /etc/cresignsys/lib/database.sh

9. Require Root

require_root

10. Validate Argument

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

DOMAIN="$(normalize_domain "$1")"

validate_domain "$DOMAIN"

11. Determine Expected Web Root

Your CHP convention is:

/storage/websites/DOMAIN/public

Therefore initially:

WEB_ROOT="${WEB_ROOT}/${DOMAIN}/public"

But don’t assume this forever.

Existing websites may have different layouts.


12. Discovery Should Search First

Check:

if [[ -d "$WEB_ROOT" ]]; then
    echo "[OK] Web root found"
else
    echo "[FAIL] Web root not found"
fi

For example:

/storage/websites/example.com/public

13. Don’t Create Missing Directories

This command:

hosting-db-import

must never do:

mkdir -p

for a missing site.

If the directory doesn’t exist:

IMPORT FAILED

not:

DIRECTORY CREATED

14. Discover Ownership

Run:

stat -c '%U:%G' "$WEB_ROOT"

Example:

www-data:www-data

Store:

site_user=www-data

and:

site_group=www-data

If your CHP architecture uses one dedicated Linux user per site, record that instead.


15. Discover WordPress

Check:

[[ -f "$WEB_ROOT/wp-config.php" ]]

If present:

WordPress:
DETECTED

Otherwise:

WordPress:
NOT DETECTED

This matters because CHP should eventually support more than WordPress.


16. Don’t Assume Every Site Is WordPress

Your hosting platform may eventually host:

WordPress
Laravel
PHP application
Static HTML
Node.js
Python

Therefore the database should eventually contain:

application_type

Possible values:

WORDPRESS
PHP
STATIC
LARAVEL
NODE
PYTHON
CUSTOM
UNKNOWN

For the current migration:

WORDPRESS

if detected.


17. Add Application Type

Eventually:

ALTER TABLE sites
ADD COLUMN application_type TEXT NOT NULL DEFAULT 'UNKNOWN';

Example:

example.com
application_type=WORDPRESS

18. Discover WordPress Version

If WordPress exists:

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

Example:

6.8.2

Store:

wordpress_version=6.8.2

Don’t install or update anything during import.


19. Discover WordPress Database

You can use:

sudo -u "$SITE_USER" \
wp --path="$WEB_ROOT" config get DB_NAME

Then:

wp --path="$WEB_ROOT" config get DB_USER

and:

wp --path="$WEB_ROOT" config get DB_HOST

20. Do Not Store Database Password in CHP Metadata

This is important.

Do not copy:

DB_PASSWORD

into:

sites

or:

services

The WordPress application already has its credentials.

CHP only needs enough information to identify and verify the application.


21. Database Metadata

Store:

database_name
database_host

if necessary.

Do not store:

database_password

in the CHP database.


22. MySQL Verification

After discovering the database:

wp --path="$WEB_ROOT" db check

If successful:

Database:
OK

If it fails:

Database:
ERROR

But don’t modify the database.


23. Discover PHP Version

There are several possible sources.

First inspect the Nginx configuration for the PHP-FPM socket:

fastcgi_pass unix:/run/php/php8.3-fpm.sock;

or:

fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock;

Extract:

8.3

24. Don’t Guess PHP Version From CLI

This:

php -v

only tells you the CLI PHP version.

It may not be the version used by Nginx.

For example:

CLI:
PHP 8.3

Website:
PHP 8.2

Therefore CHP should discover the actual PHP-FPM configuration.


25. Discover PHP-FPM Socket

Search the site’s Nginx configuration.

For example:

grep -R "fastcgi_pass" /etc/nginx -n

Then identify:

example.com
      ↓
PHP socket
      ↓
php8.3-fpm

26. Discover Nginx Configuration

Search:

sudo nginx -T

and identify the server_name associated with:

example.com

The importer should record:

nginx:
DETECTED

and ideally:

nginx_config_path

27. Don’t Store Arbitrary Nginx Configuration in the Database

Avoid putting the complete Nginx configuration into:

sites

The database should store metadata such as:

nginx_config_path

The actual configuration remains on disk.


28. Discover Domain Names

The primary domain may be:

example.com

But Nginx may contain:

server_name example.com www.example.com;

The importer should discover:

example.com
www.example.com

and compare them with the database.


29. Domain Classification

For the initial importer:

example.com

can become:

PRIMARY

and:

www.example.com

can become:

ALIAS

However, don’t automatically classify every secondary hostname as an alias if its Nginx behavior is different.

Inspect redirects.


30. Redirect Detection

If:

return 301 https://example.com$request_uri;

exists for:

www.example.com

then its type might be:

REDIRECT

rather than:

ALIAS

31. DNS Discovery

Run:

dig +short A "$DOMAIN"

and:

dig +short AAAA "$DOMAIN"

Then compare with:

SERVER_IPV4
SERVER_IPV6

Set:

dns_state

to:

CORRECT
WRONG
PENDING
MULTIPLE
UNKNOWN

32. Don’t Change DNS

Again:

hosting-db-import

does not create DNS records.

It only discovers them.


33. SSL Discovery

Check whether the site has:

/etc/letsencrypt/live/example.com/

or another certificate configuration.

Determine:

certificate:
FOUND

Then inspect:

issuer
expiry
domains

34. Certificate Expiry

For example:

Expires:
2026-10-12

Calculate:

days_remaining

Then:

> 30 days → ACTIVE
7-30 days → EXPIRING_SOON
< 7 days → WARNING
expired → EXPIRED

Use the actual date arithmetic rather than hard-coding a date.


35. SSL Certificate Table

The importer can create:

ssl_certificates

record:

site_id=1
certificate_path=/etc/letsencrypt/live/example.com/fullchain.pem
private_key_path=/etc/letsencrypt/live/example.com/privkey.pem
issuer=Let's Encrypt
expires_at=...
status=ACTIVE

Again, the private key itself is not copied into the CHP database.


36. Certificate Domain Mapping

If the certificate contains:

example.com
www.example.com

create:

certificate_domains

relationships.


37. Discover Backup Configuration

Check:

/backup/cresignsys/example.com/

if that is your CHP backup location.

If backups exist:

Backup:
DETECTED

Then import backup metadata.


38. Don’t Assume Every Backup Is Valid

A file existing doesn’t mean the backup is valid.

Initially record:

status=DISCOVERED

Then:

hosting-backup-verify

can later change it to:

VERIFIED

39. Backup Discovery

For each backup directory:

2026-08-13_160000

collect:

path
size
timestamp
type

Do not perform a full restore during import.


40. Discover Site State

Determine:

ACTIVE
SUSPENDED
UNKNOWN

by inspecting CHP state/configuration if it already exists.

If no state exists:

management_state=IMPORTED
service_state=UNKNOWN

Do not guess.


41. Discovery Report

Before writing anything to the database, print:

CresignSys Site Discovery
=========================

Domain:
example.com

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

Owner:
 www-data:www-data

Application:
 WordPress

WordPress:
 6.8.2

PHP-FPM:
 8.3

Nginx:
 DETECTED

DNS:
 CORRECT

SSL:
 ACTIVE

Database:
 wp_example

Backups:
 12 found

Then:

Ready for import.

42. Add --dry-run

The command should support:

sudo hosting-db-import example.com --dry-run

This performs:

DISCOVERY

but doesn’t write to the database.


43. Dry-Run Output

CresignSys Database Import
==========================

Mode:
DRY RUN

Site:
example.com

[OK] Web root
[OK] WordPress
[OK] PHP-FPM
[OK] Nginx
[OK] DNS
[OK] SSL
[OK] Database

No database changes made.

This is extremely useful.


44. Normal Import

Then:

sudo hosting-db-import example.com

performs:

DISCOVER
   ↓
VALIDATE
   ↓
DATABASE TRANSACTION
   ↓
CREATE SITE
   ↓
CREATE DOMAINS
   ↓
CREATE SERVICES
   ↓
CREATE SSL
   ↓
CREATE BACKUPS
   ↓
COMMIT

45. Database Transaction

The import should use:

BEGIN;

Then:

INSERT sites
INSERT domains
INSERT services
INSERT SSL
INSERT backups
INSERT operation

Finally:

COMMIT;

If something fails:

ROLLBACK;

46. Record the Import Operation

Create:

operations

record:

operation=IMPORT
status=SUCCESS
message=Existing site imported

This gives you an audit trail.


47. Idempotency

If the site is already imported:

sudo hosting-db-import example.com

should not create a duplicate.

Instead:

Site already exists in CHP database.

Then offer:

Use --refresh to rediscover metadata.

48. Add --refresh

sudo hosting-db-import example.com --refresh

means:

Existing CHP record
       ↓
Rediscover actual server state
       ↓
Compare
       ↓
Update metadata

Still don’t modify the website.


49. Refresh Is Not Repair

This distinction is essential.

hosting-db-import example.com --refresh

means:

Update CHP’s understanding of the site.

While:

hosting-repair example.com

means:

Change the server to restore the desired state.


50. Import Safety Modes

Eventually support:

--dry-run
--refresh
--force

But don’t implement --force yet.

A safe first version only needs:

default
--dry-run
--refresh

51. Example Existing Site

Suppose:

/storage/websites/learn.cresignsys.com/public

contains:

wp-config.php
wp-admin/
wp-content/
wp-includes/

Nginx contains:

server_name learn.cresignsys.com;

and PHP uses:

php8.3-fpm

The importer discovers:

Site:
learn.cresignsys.com

Application:
WORDPRESS

PHP:
8.3

Nginx:
YES

Database:
YES

SSL:
YES

52. Database Record

The site could become:

sites

id: 1
primary_domain: learn.cresignsys.com
web_root: /storage/websites/learn.cresignsys.com/public
php_version: 8.3
site_user: www-data
application_type: WORDPRESS
management_state: VERIFIED
service_state: ACTIVE
health_state: HEALTHY

53. Domain Record

domains

id: 1
site_id: 1
domain: learn.cresignsys.com
domain_type: PRIMARY
dns_state: CORRECT
ssl_state: ACTIVE

54. Service Records

services

site_id | service_name | status | version
------------------------------------------
1       | NGINX        | OK     |
1       | PHP_FPM      | OK     | 8.3
1       | MYSQL        | OK     | 8.0
1       | WORDPRESS    | OK     | 6.x

55. Import Verification

After database insertion, run a read-only verification:

DATABASE
   ↓
Expected:
learn.cresignsys.com
   ↓
LIVE SERVER
   ↓
Actual:
learn.cresignsys.com

If they match:

VERIFIED

56. Management State

Only after successful verification:

management_state=VERIFIED

Later, when you explicitly allow CHP to modify the site:

management_state=MANAGED

This is a valuable safety boundary.


57. Why MANAGED Matters

Suppose you import:

existing-client-site.com

You may want CHP to monitor it but not automatically modify it.

Therefore:

VERIFIED

can mean:

CHP understands the site.

while:

MANAGED

means:

CHP is authorized to control its configuration.


58. Site Management Policy

Add eventually:

management_policy

with:

READ_ONLY
MONITOR
MANAGED

For initial imports:

READ_ONLY

is safest.


59. Example

existing-site.com

Management:
READ_ONLY

Health:
HEALTHY

CHP can show:

DNS
SSL
PHP
Nginx
WordPress
Backup

but automatic repair is disabled.


60. Explicit Adoption

Later introduce:

sudo hosting-adopt example.com

This would mean:

READ_ONLY
     ↓
MANAGED

after explicit confirmation.

This is safer than automatically taking control of an existing website.


61. Adoption Workflow

hosting-adopt example.com
        ↓
Show discovered configuration
        ↓
Show what CHP will manage
        ↓
Require confirmation
        ↓
Create CHP templates/state
        ↓
Set MANAGEMENT_POLICY=MANAGED

62. What Adoption Should NOT Do Automatically

It should not immediately:

change DNS
renew SSL
upgrade PHP
update WordPress
change permissions
delete unknown files

Adoption means:

CHP takes ownership of the site’s management metadata.

Not:

CHP changes everything.


63. Discovery Report for Adoption

Example:

CresignSys Site Adoption
========================

Domain:
example.com

Current PHP:
8.2

Current Nginx:
Detected

Current SSL:
Valid until 2026-11-04

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

Current Owner:
www-data:www-data

CHP Management:
READ-ONLY

No changes will be made during discovery.

Then:

Use:
hosting-adopt example.com

when you’re ready.


64. Drift Detection

After import, CHP can periodically compare:

DATABASE

against:

LIVE SERVER

Example:

Database:
PHP 8.3

Nginx:
PHP 8.2

Result:

DRIFT

65. hosting-reconcile

This leads to a new command:

sudo hosting-reconcile example.com

It should initially be read-only.

Output:

CresignSys Reconciliation
=========================

PHP
  CHP:    8.3
  Actual: 8.2
  Result: DRIFT

DNS
  CHP:    EXPECTED_SERVER
  Actual: EXPECTED_SERVER
  Result: MATCH

SSL
  CHP:    ACTIVE
  Actual: ACTIVE
  Result: MATCH

Overall:
DRIFT DETECTED

66. Reconciliation Is Different From Repair

hosting-reconcile

asks:

What is different?

While:

hosting-repair

asks:

How do we fix it?

This separation prevents accidental modifications.


67. Current Migration Architecture

                  EXISTING SITE
                       │
                       ▼
                 DISCOVERY
                       │
                       ▼
                 DRY RUN
                       │
                       ▼
                 IMPORT
                       │
                       ▼
                 VERIFY
                       │
                       ▼
                READ-ONLY
                       │
             explicit adoption
                       │
                       ▼
                  MANAGED

68. Recommended Migration Order

For your existing server, don’t import all websites at once.

Use:

1. One test website
2. Verify database
3. Verify status
4. Verify DNS
5. Verify SSL
6. Verify WordPress
7. Verify backup
8. Import second site
9. Repeat
10. Migrate remaining sites

69. First Test Site

Use a non-critical site first.

For example:

sudo hosting-db-import learn.cresignsys.com --dry-run

Then:

sudo hosting-db-import learn.cresignsys.com

Then:

sudo hosting-info learn.cresignsys.com

Then:

sudo hosting-site-status learn.cresignsys.com

70. Verify Database

Run:

sqlite3 /var/lib/cresignsys/chp.db

Then:

SELECT id, primary_domain, management_state
FROM sites;

Expected:

1|learn.cresignsys.com|VERIFIED

71. Verify Domains

SELECT site_id, domain, domain_type, dns_state, ssl_state
FROM domains;

Expected:

1|learn.cresignsys.com|PRIMARY|CORRECT|ACTIVE

72. Verify Services

SELECT site_id, service_name, status, version
FROM services;

Expected:

1|NGINX|OK|
1|PHP_FPM|OK|8.3
1|MYSQL|OK|8.0
1|WORDPRESS|OK|

73. Verify Operations

SELECT id, site_id, operation, status
FROM operations
ORDER BY id DESC;

Expected:

1|1|IMPORT|SUCCESS

74. The First Migration Goal

After successful import:

/storage/websites/learn.cresignsys.com/public

must be unchanged.

Nginx:

UNCHANGED

PHP:

UNCHANGED

MySQL:

UNCHANGED

WordPress:

UNCHANGED

Only:

/var/lib/cresignsys/chp.db

and CHP logs should change.


75. This Is the Golden Migration Rule

READ EXISTING INFRASTRUCTURE
        ↓
UNDERSTAND IT
        ↓
RECORD IT
        ↓
VERIFY IT
        ↓
ONLY THEN MANAGE IT

Never reverse this order.


76. Updated CHP Architecture

                         CHP
                          │
              ┌───────────┴───────────┐
              ▼                       ▼
        CONTROL PLANE             DATA PLANE
              │                       │
              ▼                       ▼
       CHP SQLite DB            Existing Website
              │                       │
       ┌──────┼──────┐          ┌─────┼─────┐
       ▼      ▼      ▼          ▼     ▼     ▼
     Sites Domains Services    Nginx PHP  MySQL
       │      │      │          │     │     │
       └──────┼──────┘          └─────┼─────┘
              │                       │
              └──────────┬────────────┘
                         ▼
                    Reconciliation
                         │
                         ▼
                     Site Status

77. Lesson 079 — Core Principle

The existing server should not be rebuilt just to introduce CHP.

Instead:

EXISTING WEBSITES
       ↓
DISCOVER
       ↓
IMPORT
       ↓
VERIFY
       ↓
OPTIONALLY ADOPT
       ↓
CHP MANAGEMENT

This allows CresignSys Hosting Platform to grow around the websites you already have.


Next Lesson — 080

Build hosting-db-import Properly

The next lesson will turn the design above into the actual migration implementation.

We will build the discovery engine in stages:

hosting-db-import
       │
       ├── 1. Validate domain
       ├── 2. Locate web root
       ├── 3. Detect owner
       ├── 4. Detect WordPress
       ├── 5. Detect database
       ├── 6. Detect PHP-FPM
       ├── 7. Detect Nginx
       ├── 8. Detect DNS
       ├── 9. Detect SSL
       ├── 10. Detect backups
       ├── 11. Generate discovery report
       ├── 12. Dry-run verification
       └── 13. Transactional database import

The important implementation goal will be safe discovery first, database writing second.

Comments

Leave a Reply

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