Installation & Hosting
Local
Configure Virtual Hosts for Local Development
For an optimal development experience that perfectly simulates a production environment, we strongly recommend configuring Virtual Hosts.
This will allow you to access your sites via clean, realistic URLs (e.g., http://my_site_1.local).
The process is done in 3 simple steps and takes only 5 minutes.
Prerequisites
Step 1: Modify your hosts file
The hosts file is your computer's address book. We will tell it that your test site domains point to your local machine (127.0.0.1).
- Open Notepad as an administrator (right-click icon > "
Run as administrator"). - In Notepad, go to "File" → Open, and open the file:
C:\\Windows\\System32\\drivers\\etc\\hosts(remember to select "All files (*.*)" to display the hosts file). - At the end of the file, add a line for each domain:
# Pragma CMS Local Development Sites
127.0.0.1 platform.pragma-cms.local
127.0.0.1 mon-site-1.local
127.0.0.1 mon-site-2.local
- Save and close the file.
- Open the Terminal application.
- Type the following command to open the hosts file with administrator rights and press Enter:
sudo nano /etc/hosts - Enter your macOS session password when prompted.
- At the end of the file, add a line for each domain:
# Pragma CMS Local Development Sites
127.0.0.1 platform.pragma-cms.local
127.0.0.1 my-site-1.local
127.0.0.1 my-site-2.local
- To save, press Ctrl + O, then Enter. To quit, press Ctrl + X.
Step 2: Configure Virtual Hosts in Apache
Now, we will tell Apache how to handle requests for these new domains.
- In the XAMPP control panel, click
Config(for Apache) >httpd.conf. - In the file, search for the line Include
conf/extra/httpd-vhosts.conf. - Remove the # to enable the line, then save and close the file.
- Open the
httpd-vhosts.conffile:C:\xampp\apache\conf\extra\httpd-vhosts.conf - Add the following block, replacing "
C:/xampp/htdocs/pragma-cms" with the actual path to your project:
💡Important: All domains must point to the same /public folder of Pragma CMS.
# Virtual Host for the platform
DocumentRoot "C:/xampp/htdocs/pragma-cms/public"
ServerName platform.pragma-cms.local
AllowOverride All
Require all granted
# Virtual Host for my-site-1.local
DocumentRoot "C:/xampp/htdocs/pragma-cms/public"
ServerName mon-site-1.local
AllowOverride All
Require all granted
- Save and close the file.
- In the MAMP interface, go to the menu
File > Edit Template > Apache > httpd.conf. - In the file, search for the line
#Include /Applications/MAMP/conf/apache/extra/httpd-vhosts.conf. - Remove the
#to enable the line, then save and close the file. - Go back to the menu
File > Edit Template > Apache > extra/httpd-vhosts.conf. - Add the following block, replacing
/Applications/MAMP/htdocs/pragma-cmswith the actual path to your project:
💡 Important: All domains must point to the same/publicfolder of Pragma CMS.
# Virtual Host for the platform
DocumentRoot "/Applications/MAMP/htdocs/pragma-cms/public"
ServerName platform.pragma-cms.local
AllowOverride All
Require all granted
# Virtual Host for my-site-1.local
DocumentRoot "/Applications/MAMP/htdocs/pragma-cms/public"
ServerName mon-site-1.local
AllowOverride All
Require all granted
- Save and close the file.
Step 3: Enable Error Display
For a local environment, it is often necessary to enable display_errors=On in php.ini to see errors in the browser.
Step 4: Restart Apache
For the changes to take effect, restart the servers from the XAMPP or MAMP control panel.
- Click
Stop Servers(orStopnext to Apache). - Wait for a complete stop.
- Click
Start Servers(orStart).
You're all set!
You can now open your browser and visit your sites directly:
- http://platform.pragma-cms.local
- http://my-site-1.local
- http://my-site-2.local
Your local installation now behaves exactly like a production server.
Production
This guide details the steps to deploy Pragma CMS Platform on a dedicated server/VPS. This architecture is designed for performance, security, and multi-site management (SaaS).
1. Rent the Server (The VPS)
Get a VPS from a reputable provider (OVH, Hetzner, DigitalOcean, etc.).
- Recommended config to start: 2 vCPU, 4GB RAM (to comfortably run PHP/MySQL).
- OS: Choose an Operating System (OS).
- For professional hosting, we recommend using a Linux distribution. You have two main choices:
- Ubuntu LTS (Recommended for beginners):
✅ Pros: Very popular, extensive documentation, easy access to recent PHP versions, simplified configuration.
❌ Cons: Slightly heavier than Debian (includes more tools by default). - Debian (The purist's choice):
✅ Pros: Legendary stability, extremely lightweight ("Bare metal"), 100% open source.
❌ Cons: Slower update cycle, sometimes requires more manual configuration.
- Ubuntu LTS (Recommended for beginners):
Note: This guide will use commands for Ubuntu 22.04 / 24.04 LTS, but they are 99% compatible with Debian.
2. Securing the VPS
- Create a user (with your name) if not already done (never stay as root)Working as root is dangerous. If a script or a hacker gets in, they have full rights. We will create an administrator user.
- Connect as root one last time:
ssh root@YOUR_IP
- Create a new user (replace
my_pseudowith your desired name):
adduser my_pseudo
# (Choose a strong password, leave the rest blank by pressing Enter)
- Give them sudo rights:
usermod -aGsudo my_pseudo
- SSH Keys (The Unpickable Lock)
Passwords can be guessed (brute force), but cryptographic SSH keys cannot.- On your personal PC (not the server), open a terminal (PowerShell or CMD on Windows, Terminal on Mac/Linux).
- Generate a key pair (if you don't already have one):
ssh-keygen -t ed25519 -C"admin@pragma-cms"
# Press Enter for everything or set a passphrase for added security
- Send the public key to your server:
# On Linux/Mac:
ssh-copy-id my_pseudo@YOUR_VPS_IP
# On Windows (if ssh-copy-id does not exist):
type$env:USERPROFILE\.ssh\id_ed25519.pub | ssh my_pseudo@YOUR_VPS_IP"mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
- Test immediately:
ssh my_pseudo@YOUR_VPS_IP
If the connection is made without asking for a password (or only the key's passphrase), you've succeeded.
3. Installation Method
Automatic Installation (Recommended)
- Download the script from your computer to the VPS:
scp -P 22 /chemin/local/to/scripts/setup/install_server.sh my_pseudo@YOUR_IP:~
- Launch the installation on the VPS:
sudo bash install_server.sh
- Follow the on-screen instructions.
- Once finished, continue to step 4 (File Deployment).
Manual Installation (Advanced)
- Lock Down SSH (Change the lock)
Now that you have your key, let's forbid passwords and close the root door.- On the server (connected as
my_pseudo), edit the SSH configuration:
- On the server (connected as
sudo nano /etc/ssh/sshd_config
- Modify or add the following lines:
# 1. Change the port (security by obscurity, reduces 99% of bot logs)
Port 54321 # Choose a number between 1024 and 65535 and note it down!
# 2. Forbid direct root login
PermitRootLogin no
# 3. Forbid passwords (SSH key mandatory)
PasswordAuthentication no
PermitEmptyPasswords no
# 4. Limit to authorized users
AllowUsers my_pseudo
- Save (Ctrl+X, Y, Enter).
⚠️ DO NOT RESTART SSH YET. We must configure the firewall first.
- Preventive Step: Avoid Configuration Conflicts (Cloud-init & Systemd)
Before going further, let's check that nothing will prevent the port change. This is a frequent problem on modern VPS providers (OVH, AWS).- Check for conflicting configuration files
Some providers forceport 22via additional files. If they exist, they will overwrite your change.
- Check for conflicting configuration files
ls /etc/ssh/sshd_config.d/
.conf files (like 50-cloud-init.conf), delete them: sudo rm /etc/ssh/sshd_config.d/*.conf
- Check Socket activation (Ubuntu 22.04+)
On recent versions, SSH can be managed by asystemd socket, which ignores your port configuration. Let's disable it preventively to give control back to the classic SSH service.
# Check if the socket is active
systemctl is-active ssh.socket
active, run these commands to disable it: sudo systemctl stop ssh.socket
sudo systemctl disable ssh.socket
- Firewall (UFW – The Bouncer)
UFW will close all ports except for the Web and your new SSH port.
# 1. Install UFW (often already present)
sudo apt install ufw -y
# 2. Default rules: block all incoming, allow all outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing
# 3. Allow your new SSH port
sudo ufw allow 54321/tcp
# 4. Allow Web (80 and 443)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# 5. Enable the firewall
sudo ufw enable
# (Answer 'y' to the confirmation)
- Restart SSH after configuration:
sudo systemctl restart ssh
- Critical test: open a new terminal and try to connect:
ssh -p 54321 my_pseudo@YOUR_VPS_IP
- Fail2Ban (The Watchdog)
Even with a changed port, bots will try to scan your server. Fail2Ban automatically bans IPs that make too many errors.
# 1. Installation
sudo apt install fail2ban -y
# 2. Local configuration
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
- In this file, search for the
[sshd]section (often around line 280) and modify the port:
[sshd]
enabled = true
port = 54321 # Your SSH port here
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 3 ; To be added generally
bantime = 1h ; To be added generally
findtime = 10m
- Save (press Ctrl+X to exit, then Y to confirm, followed by Enter) and restart:
sudo systemctl restart fail2ban
- Automatic Updates (Passive Security)
For a commercial CMS, you don't want to miss a critical Linux vulnerability.
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades
# Choose "Yes" (Enter)
- Final clean-up (Optional): Once you’ve logged in with your new username
my_pseudo, you can delete the default user for added security:sudo userdel -r ubuntu.
ssh -p 54321 ...), it means you may have effectively “locked yourself out”.
Don’t worry — here is the procedure to regain access via the recovery console. - Access the KVM Console (Virtual Screen)
Log in to your hosting provider dashboard (e.g., OVH) and launch the KVM (or VNC) console. This is a window that simulates the physical screen of the server. - Fix Keyboard Layout (QWERTY vs AZERTY)
By default, the KVM console is often configured in English (QWERTY).- Log in with your user (
ubuntuor your new user). - Tip: If your password contains an “a”, type “q”. If “z”, type “w”. “m” is located where “,” is on AZERTY keyboards.
- Log in with your user (
sudo dpkg-reconfigure keyboard-configuration
- Keyboard model: Select "Generic 105-key PC"
- Country: Select "Other", then "French"
- Layout: Select "French (default)"
- Leave the other options as default
sudo systemctl restart keyboard-setup
- Diagnose the issue
Check which port SSH is actually listening on:
sudo ss -ltnp | grep ssh
- If you see
0.0.0.0:22: the server is still listening on port 22 (your change was not applied). Follow Solution A, then B, then C. - If you see
0.0.0.0:54321: SSH is correctly listening on the new port; the issue is likely the firewall. Proceed to the firewall section below.
Make sure the
# has been removed from the Port line. sudo nano /etc/ssh/sshd_config
# Ensure the line is: Port 54321 (not #Port 54321)
On modern cloud images, default configuration files may force port 22 and override your settings. These can override your configuration and should be removed.
Check if they exist:
ls /etc/ssh/sshd_config.d/
50-cloud-init.conf or 60-cloudimg-settings.conf, remove them:
sudo rm /etc/ssh/sshd_config.d/*.conf
sudo systemctl restart ssh
sudo ss -ltnp | grep ssh. If it shows :54321, try connecting again from your local machine. If port 22 persists, systemd is handling SSH via a socket instead of the SSH service itself. You must disable it to restore control to SSH.
# Stop the socket listener on port 22
sudo systemctl stop ssh.socket
# Prevent it from starting at boot
sudo systemctl disable ssh.socket
# Restart SSH
sudo systemctl restart ssh
If SSH is listening on the correct port but connections still fail, open the port in the firewall:
sudo ufw allow 54321/tcp
sudo ufw reloaddrag_indicatorformat_indent_increase× You should now be able to connect from your local terminal.
ssh root@your-ip) and run the following commands. 💡 To avoid adding
sudo every time, type sudo -i.
- Step A: System Update
apt update && apt upgrade -y
apt install -y curl zip unzip git software-properties-common
- Step B: Web Server Installation (Choose Caddy, Nginx, or Apache)
Caddy is not available in the default package repositories, so it must be added manually (official commands):
# Add Caddy security keys
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
# Installation
apt update
apt install caddy -y
- Step C: Installing PHP 8.4 (or higher)
# Update package lists
apt update
# Install PHP and required dependencies
apt install -y php-fpm php-mysql php-curl php-gd php-intl php-mbstring php-xml php-zip php-imagick ghostscript
- Step D: MariaDB Installation (Database)
apt install mariadb-server -y
mysql_secure_installation # Answer Y to everything to secure
- Open PHP configuration:
# Adjust the version number (8.4 or later) depending on what you have installed
sudo nano /etc/php/8.4/fpm/conf.d/99-pragma.ini
- Add the content:
; ============================
; Pragma OPcache Configuration
; ============================
[opcache]
; Enable OPcache
opcache.enable=1
; Memory allocation
opcache.memory_consumption=256 ; Increased to 256MB (comfortable for a 8GB VPS)
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000 ; Increased to handle core system + client sites
; BALANCED CONFIGURATION (PERFORMANCE + FLEXIBILITY)
opcache.validate_timestamps=1 ; Tells PHP to check if files have changed
opcache.revalidate_freq=0 ; Revalidate on every request (instant updates)
opcache.save_comments=1 ; Keep comments (required for some CMS internal logic)
- Save and exit: Ctrl+X, Y, Enter.
- Apply changes:
sudo systemctl restart php8.4-fpm
To ensure the security of your tests, we will create two separate databases: one for Production and one for Staging (Tests).
Connect to MySQL:
sudo mysql
MariaDB [(none)]>), type the following: -- PRODUCTION DATABASE
CREATE DATABASE pragma_cms_platform CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- STAGING DATABASE
CREATE DATABASE pragma_cms_platform_staging CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- Create the ‘Master’ user (who will be able to create customer databases)
-- Replace “MySuperPassword” with a real, strong password!
CREATE USER 'pragma_master'@'localhost' IDENTIFIED BY 'MySuperPassword';
-- Give him full access to ALL databases (as he needs to create websites)
GRANT ALL PRIVILEGES ON *.* TO 'pragma_master'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;
EXIT;
Run these commands one by one on your VPS:
- Installing the ACL tool
# Installing the ACL (Access Control Lists) tool
# Essential for fine-grained permission management without compromising security
sudo apt install acl -y
- Creating directories for the “pragma-cms” production environment
# 1. Creating the directory structure (the folders currently belong to root)
sudo mkdir -p /var/www/pragma-cms/public
sudo mkdir -p /var/www/pragma-cms/public/assets/cms-core
sudo mkdir -p /var/www/pragma-cms/cms-versions
sudo mkdir -p /var/www/pragma-cms/sites
sudo mkdir -p /var/www/pragma-cms/storage/tmp
sudo mkdir -p /var/www/pragma-cms/storage/cache
sudo mkdir -p /var/www/pragma-cms/storage/logs
sudo mkdir -p /var/www/pragma-cms/scripts/cli
sudo mkdir -p /var/www/pragma-cms/scripts/cron
sudo mkdir -p /var/www/pragma-cms/scripts/setup
# 2. Basic standard configuration
# Set the web server’s ownership to the www-data group and apply standard permissions (755)
sudo chown -R www-data:www-data /var/www/pragma-cms
sudo chmod -R 755 /var/www/pragma-cms
# 3. APPLYING EXTENDED PERMISSIONS (ACL)
# This step is crucial: it ensures that YOU (your SSH user: replace $USER here)
# and PHP (www-data) will always be able to read and write files,
# regardless of who created them.
# A. Apply to existing folders
sudo setfacl -R -m u:www-data:rwx /var/www/pragma-cms
sudo setfacl -R -m u:$USER:rwx /var/www/pragma-cms
# B. Apply default permissions (-d)
# This forces all FUTURE files created (by PHP or VS Code)
# to automatically inherit these permissions, even if PHP creates a directory with 0755 permissions.
sudo setfacl -R -d -m u:www-data:rwx /var/www/pragma-cms
sudo setfacl -R -d -m u:$USER:rwx /var/www/pragma-cms
- Creating directories for the staging “pragma-cms-staging”
# 1. Creating the directory structure (folders are initially owned by root)
sudo mkdir -p /var/www/pragma-cms-staging/public
sudo mkdir -p /var/www/pragma-cms-staging/public/assets/cms-core
sudo mkdir -p /var/www/pragma-cms-staging/cms-versions
sudo mkdir -p /var/www/pragma-cms-staging/sites
sudo mkdir -p /var/www/pragma-cms-staging/storage/tmp
sudo mkdir -p /var/www/pragma-cms-staging/storage/cache
sudo mkdir -p /var/www/pragma-cms-staging/storage/logs
sudo mkdir -p /var/www/pragma-cms-staging/scripts/cli
sudo mkdir -p /var/www/pragma-cms-staging/scripts/cron
sudo mkdir -p /var/www/pragma-cms-staging/scripts/setup
# 2. Basic standard configuration
# Set ownership to the web server group (www-data) and apply standard permissions (755)
sudo chown -R www-data:www-data /var/www/pragma-cms-staging
sudo chmod -R 755 /var/www/pragma-cms-staging
# 3. APPLY EXTENDED PERMISSIONS (ACL)
# This step is critical: it ensures that YOU (your SSH user: replace $USER)
# and PHP (www-data) can always read and write files,
# regardless of who created them.
# A. Apply to existing directories
sudo setfacl -R -m u:www-data:rwx /var/www/pragma-cms-staging
sudo setfacl -R -m u:$USER:rwx /var/www/pragma-cms-staging
# B. Apply default inheritance (-d)
# This ensures all FUTURE files created (by PHP or VS Code)
# automatically inherit these permissions, even if PHP creates them with 0755.
sudo setfacl -R -d -m u:www-data:rwx /var/www/pragma-cms-staging
sudo setfacl -R -d -m u:$USER:rwx /var/www/pragma-cms-staging
public/ folder. We will proceed in two stages: first, a test configuration to check that everything works via your IP address, followed by the final production configuration. - Step A: Create a test file
Before configuring the server, let's create a small file to verify that PHP is working correctly.
You can create this file in two ways:- Via VS Code:
In the left file explorer, right-click thepublicfolder → New File, name ittest.php, and paste the content below. - Via terminal:
- Via VS Code:
echo"" > /var/www/pragma-cms/public/test.php
- Step B: Temporary configuration (IP test)
We will configure Caddy (recommended) to serve the site using your server IP address, without HTTPS for now.- Open the Caddy configuration file
/etc/caddy/Caddyfile(via VS Code orsudo nano /etc/caddy/Caddyfile). - Replace its content with the following (use your real IP):
- Open the Caddy configuration file
# Replace X.X.X.X with your VPS IP
http://X.X.X.X {
root * /var/www/pragma-cms/public
encode gzip
file_server
# PHP 8.4
php_fastcgi unix//run/php/php8.4-fpm.sock
}
- Apply the configuration:
sudo systemctl reload caddy
- Test it! Open your browser and go to:
http://VOTRE_IP/test.php.- ✅ Success: If you see the purple PHP info page, your server is working correctly.
- ❌ Error 502: Caddy cannot reach PHP. Ensure PHP 8.4 FPM is installed and running.
- Step C: Final configuration (Production & HTTPS)
Now that the server works, we move to the production setup that handles domains and automatic SSL.- Remove the test file
rm /var/www/pragma-cms/public/test.php
- Generate a basic auth hash for staging access
Generate a password hash using:
sudo caddy hash-password
This will be used to protect staging environments like:
staging-site-test.mon-site.com
- Replace the entire Caddy configuration
Replace/etc/caddy/Caddyfilewith the following:
# =========================================================================
# == SNIPPETS (Reusable configurations)
# =========================================================================
# Common CMS configuration (Security, PHP, Assets)
(cms_common) {
# 1. Web root (argument {args[0]} passed when importing)
root * {args[0]}/public
# 2. ASSET / MEDIA / UPLOAD / THEME HANDLING (core logic)
# If a file is requested in /assets or /media and does NOT exist,
# redirect to asset handler script
@missing_assets {
path /assets/* /media/* /uploads/* /themes/*
not file {path}
}
rewrite @missing_assets /assets/serve-site-assets.php?{query}
# 3. PHP FastCGI (Script execution) – Make sure it’s compatible with your version of PHP!
php_fastcgi unix//run/php/php8.4-fpm.sock
# 4. Front Controller (main routing)
# If the file does not exist, everything is redirected to index.php
try_files {path} {path}/ /index.php?{query}
# 5. Compression and Static File Server
encode gzip zstd
file_server
}
# =========================================================================
# == SERVER BLOCKS
# =========================================================================
# 1. Global SSL (on-demand TLS)
{
on_demand_tls {
ask http://localhost:8080/check-domain
}
}
# 2. Internal server for check-domain (inaccessible from outside)
# This server is only accessible by Caddy itself (localhost)
# It is used solely to respond to the ‘ask’
http://localhost:8080 {
root * /var/www/pragma-cms/public
# We use php_fastcgi just for this specific route
route /check-domain {
php_fastcgi unix//run/php/php8.4-fpm.sock
}
}
# 3. PROD PLATFORM (Super Admin / Main Dashboard)
# Caddy will manage this certificate automatically and ‘for free’ (without involving PHP)
NOM_DE_DOMAINE_DE_LA_PLATEFORME {
# Import the shared configuration with the PROD path
import cms_common /var/www/pragma-cms
log {
output file /var/log/caddy/platform-access.log
}
}
# 4. STAGING (Platform & Test client sites)
# Replace $USER with your username and MY_HASH with your generated hash
staging.mon-site.com,
staging-site-test.mon-site.com {
# Password protection
basic_auth {
$user MY_HASH
}
# Import the shared configuration using the STAGING path
import cms_common /var/www/pragma-cms-staging
# Google anti-indexing
header X-Robots-Tag "noindex, nofollow"
log {
output file /var/log/caddy/staging-access.log
}
}
# 5. CLIENT PRODUCTION SITES (catch-all HTTPS)
https:// {
# On-demand SSL activation
tls {
on_demand
}
# Import the shared configuration using the PROD path
import cms_common /var/www/pragma-cms
log {
output file /var/log/caddy/pragma-access.log
}
}
- Apply the final configuration:
sudo systemctl reload caddy
Apache and Nginx configurations below do not support automatic SSL generation for unknown domains. You must configure Certbot manually or use an automation script (e.g. Lua/OpenResty) if not using Caddy.
- Option A: Apache (standard)
Pragma CMS includes a pre-configured.htaccessfile in thepublic/folder. Ensuremod_rewriteis enabled.
VirtualHost Configuration:
ServerName platform.pragma-cms.com
ServerAlias *.platform.pragma-cms.com
# IMPORTANT NOTE: We are pointing directly to /public
# The .htaccess file in the project root directory is therefore ignored.
# Only the .htaccess file located WITHIN /public will be read.
DocumentRoot /var/www/pragma-cms/public
Options -Indexes +FollowSymLinks
# Enable the .htaccess file in /public (essential for routing and URL rewriting)
AllowOverride All
Require all granted
ErrorLog ${APACHE_LOG_DIR}/pragma_error.log
CustomLog ${APACHE_LOG_DIR}/pragma_access.log combined
- Option B: Nginx (performance)
Nginx does not support.htaccess. Use the following configuration in/etc/nginx/sites-available/pragma-cms:
server {
listen 80;
server_name platform.pragma-cms.com *.platform.pragma-cms.com;
root /var/www/pragma-cms/public;
index index.php;
access_log /var/log/nginx/pragma_access.log;
error_log /var/log/nginx/pragma_error.log;
# --- ASSET ROUTING ---
# Equivalent to @missing_assets in Caddy.
# If the file does not exist in /public/assets or /public/media,
# control is passed to serve-site-assets.php
location ~ ^/(assets|media|uploads|themes)/ {
try_files $uri /assets/serve-site-assets.php$is_args$args;
}
# Main router (Front Controller)
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP execution
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
# Hidden file security
location ~ /\.(?!well-known).* {
deny all;
}
}
4. Deploying CMS Files to the VPS
Now that the server is ready (via the script or manual setup), you need to upload the Pragma CMS installation files to your server.
You should have downloaded a .zip archive (the installation package) from your Pragma customer area.
- Via SFTP:
- Use a tool such as FileZilla or Cyberduck.
- Connect to your VPS (IP, username, password/key).
- Navigate to
/var/www/pragma-cms/. - Upload the contents of the
publicfolder from the archive into the server’spublic/directory. - Upload the
cms-versionsfolder from the archive into/var/www/pragma-cms/.
- Via VS Code (Remote - SSH):
- You can directly upload and edit files on the server using VS Code.
Final directory structure check:
Your server should look like this:
- 📁 /var/www/pragma-cms/
- 📁 cms-versions/ (Core system files, e.g. v1.0.0. Do not modify)
- 📁 public/ (Contains
index.php,check-domain.php(Caddy only), and assets. This is the web root) - 📁 sites/ (Empty for now, will contain your future sites)
Important:
Never modify files insidecms-versions/. This folder is managed automatically by the update system. Any changes will be overwritten during the next update.
- Prerequisites
- Install Visual Studio Code on your computer.
- Have your server credentials (IP and root password)
- Step 1: Install Remote - SSH extension
- Open VS Code.
- Click on the Extensions icon in the left sidebar (or press Ctrl+Shift+X).
- Search for
Remote - SSH(developed by Microsoft) - Click
Install.
- Step 2: Configure the connection
- Click the small green
><icon (or blue) at the bottom-left corner of the VS Code window (or press F1 and typeRemote-SSH: Connect to Host...). - Select
Connect to Host... - Select
Add New SSH Host... - Enter your SSH connection command (the same as in your terminal):
ssh -p 54321 root@YOUR_VPS_IP - Press
Enter. - Choose the first suggested configuration file (usually
C:\\Users\\YourName\\.ssh\\config or ~/.ssh/config).
- Click the small green
- Step 3: Connect to the server
- A notification appears at the bottom right:
Host added
. Click Connect. - (If the notification disappeared: Click the green icon at the bottom left >
Connect to Host...> Select the IP you just added). - A new VS Code window will open.
- Select the platform:
Linux. - Enter your server’s root password when prompted at the top of the screen.
Note: Once connected, the green icon at the bottom left will displaySSH: YOUR_IP.
- A notification appears at the bottom right:
- Step 4: Open the CMS folder
To work on your files:- In the VS Code window connected to the server, click the
Open Folderbutton in the left explorer. - The search bar at the top will ask for a path. Enter your installation path:
/var/www/pragma-cms - Click
OK. - VS Code may ask for your password again.
- You can now see the full server file structure and edit files directly.
- In the VS Code window connected to the server, click the
5. Domain Name (DNS)
Now is the time to link your domain name (e.g. my-awesome-site.com) to your VPS, so that the website can be accessed via a web browser.
- A domain name (e.g.
my-super-site.com) points to an IP address (your VPS) - A subdomain (e.g.
site1.my-super-site.com) is just another DNS entry pointing to the same IP - DNS does NOT create anything on the server, it only maps names to an IP address
Accessing DNS settings:
- Log in to your registrar (recommended: Cloudflare, or OVH, Namecheap, etc.)
- Select your domain (
my-super-site.com) - Go to the DNS management / DNS records section
Creating the main domain records:
- Create or edit an A record:
| Type | Name (Host) | Value (Target) | TTL | Note |
| A | @ | YOUR_VPS_IP | Auto | main domain |
| CNAME | www | my-super-site.com | Auto | www version |
Creating a subdomain (optional):
In most cases, a single subdomain is sufficient to start with (no need for a wildcard – see below).
- (Optional) If your management platform is hosted on a specific subdomain:
Create a second record A:- Host/Name :
site1 - Value/Target :
YOUR_VPS_IP - TTL :
Default👉 After propagation, you will be able to access: http://name1.my-super-site.com
- Host/Name :
- (Optional – Advanced) Automatically create all subdomains (Wildcard):
ℹ️ This step is not necessary to get started. Only use it if your CMS needs to handle dynamic subdomains (for demo sites, for example).
If your CMS needs to handle dynamic subdomains (e.g.name1,name2,name3, etc.), you can use a DNS wildcard.
Add an A record:- Host/Name :
* - Value/Target :
YOUR_VPS_IP - TTL :
Default
- Host/Name :
🔍 What this means:
name1.my-great-cms.comname2.my-great-cms.comtest.my-great-cms.com
👉 They will all automatically point to the VPS, without having to create a DNS record each time.
⚠️ The wildcard does not override explicitly defined subdomains (www, mail, etc.).
- Proxy (Orange Cloud):
- Make sure the cloud is Orange (Proxied) for both your records (
@
and *). This protects your IP address and enables the CDN.
- Make sure the cloud is Orange (Proxied) for both your records (
- SSL Mode (Crucial):
- Go to the
SSL/TLS>Overviewmenu. - Select
Full (Strict)mode. - Why? Your Caddy server generates its own certificates. If you remain in ‘Flexible’ mode, Cloudflare will attempt to communicate via HTTP with your server, which expects HTTPS, creating an infinite redirect loop.
- Go to the
- Verification
(It can take anywhere from 10 minutes to 24 hours, but it’s often quick).
6. Workflow and Customization
Once the platform is installed, it is important to understand where and how to work safely without breaking future updates.
Golden rule: Never modify files in cms-versions/.
This folder contains the software’s source code (the engine). If you modify it, your changes will be overwritten the next time the system is updated.
Here are the two recommended methods for working on your websites:
- Method A: Via the Control Panel (Recommended)
For 95% of tasks, you don’t need to touch the server files. Pragma CMS includes powerful tools:- Content creation: Use the Page Builder to create complex layouts.
- Design & CSS: Go to
Themes→Editor- The built-in code editor (Monaco) allows you to safely edit the CSS, JS and Twig templates of your active themes.
- The CMS automatically manages permissions and caching.
- Configuration: Everything is managed via the Site Settings.
- Method B: Via VS Code / SFTP (For developers)
If you need to develop a complex theme, add large files (fonts, videos) or debug, you can work directly on the files.
Thanks to the ACL configuration (set up in previous steps), you can create and edit files using your SSH user without blocking the web server.
Where to work?
Everything takes place in the folder:/var/www/pragma-cms/sites/
Each site you create has its own isolated folder:
/sites/
└── my-site/ <-- Site directory (identified by its “handle”)
├── assets/ <-- Global static resources (fonts, shared libraries, icons)
├── content-types/ <-- PHP definitions of the data structure (Blueprints: Page, Blog...)
├── database/ <-- Database migrations specific to this site
├── extensions/ <-- Extensions/plugins installed for this site only
├── lang/ <-- Site-specific translation files (i18n)
├── media/ <-- Media library: contains source images and generated variants
├── pages/ <-- Specific PHP controllers (custom page logic)
├── storage/ <-- Temporary files, Twig cache, system logs
├── templates/ <-- Global Twig templates (emails, basic non-theme layouts)
├── themes/ <-- This is where you work on the design
│ └── my-theme/
│ ├── assets/ (CSS, JS, theme-specific images)
│ └── templates/ (Theme .twig files)
└── uploads/ <-- Storage for miscellaneous files (PDF, Zip) or temporary files set up in previous steps
Example developer workflow:
- Connect using VS Code (Remote SSH).
- Open the folder
/var/www/pragma-cms/sites/client-xyz/themes/my-theme/. - Edit your
style.cssorhome.twigtemplates. - Save. The changes will take effect immediately online.
Note: If you create a new folder or file via VS Code, the ACL permissions you set up earlier will automatically ensure that the web server can read them. You won’t need to run any chown or chmod commands again.
7. Automated Maintenance (Cron & Backup)
- Backups: daily at 03:00 in
/var/backups/pragma-cms - Updates check: every 12 hours
- Staging deploy script available:
sudo /var/www/pragma-cms/scripts/cli/deploy_staging.sh
- 7.1 Utility scripts
- Backup script
backup.shCreate the file in the CMS directory:/var/www/pragma-cms/scripts/cron/backup.sh:
- Backup script
#!/bin/bash
set -euo pipefail
# CONFIG
BACKUP_DIR="/var/backups/pragma-cms"
DATE=$(date +"%Y-%m-%d_%H-%M")
DB_USER="pragma_master"
DB_PASS="YOUR_PASSWORD_HERE" # Replace with the actual password
KEEP_DAYS=7
# Create the backup directory
mkdir -p $BACKUP_DIR
# 1. INTELLIGENT DATABASE BACKUP
databases=$(mysql -u $DB_USER -p$DB_PASS -e "SHOW DATABASES;" -s --skip-column-names | grep -Ev "^(information_schema|performance_schema|mysql|sys)$")
for db in $databases; do
echo " - Dumping DB: $db..."
mysqldump -u $DB_USER -p$DB_PASS $db | gzip > "$BACKUP_DIR/db_${db}_$DATE.sql.gz"
done
# 2. Backup Files
tar -czf "$BACKUP_DIR/files_$DATE.tar.gz" \
--exclude='storage/cache/*' \
--exclude='storage/logs/*' \
--exclude='storage/tmp/*' \
/var/www/pragma-cms
# 3. Cleanup old backups
find $BACKUP_DIR -type f -name "*.gz" -mtime +$KEEP_DAYS -delete
- ⚠️ Security: Ensure that only root can read this script and the folder
backups:
# Make the script executable and secure (root-only read, since it contains the password)
chmod 700 /var/www/pragma-cms/scripts/cron/backup.sh
chown root:root /var/www/pragma-cms/scripts/cron/backup.sh
# Remove ACLs specifically for this sensitive file to prevent the sys_user from reading it
setfacl -b /var/www/pragma-cms/scripts/cron/backup.sh
- Staging deployment script
deploy_staging.shCreate/var/www/pragma-cms/scripts/cli/deploy_staging.sh:
#!/bin/bash
set -euo pipefail
# ===============================================================
# PRAGMA CMS - SYNC PROD -> STAGING
# ⚠️ WARNING: THIS WILL OVERWRITE THE ENTIRE STAGING
# ===============================================================
read -p "⚠️ This will overwrite the STAGING database and files with those from PROD. Continue? (y/n) " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
DB_USER="pragma_master"
read -sp "DB password ($DB_USER): " DB_PASS
echo ""
echo "🔄 1/3 Cloning the database..."
mysqldump -u $DB_USER -p$DB_PASS pragma_cms_platform | mysql -u $DB_USER -p$DB_PASS pragma_cms_platform_staging
echo "📂 2/3 Syncing files..."
rsync -av --delete \
--exclude 'config_db.php' \
--exclude 'storage/cache/*' \
--exclude 'storage/logs/*' \
/var/www/pragma-cms/ \
/var/www/pragma-cms-staging/
echo "🔧 3/3 Adjusting permissions..."
chown -R www-data:www-data /var/www/pragma-cms-staging
echo "✅ Synchronization completed!"
echo "The staging environment is now an exact clone of production."
- This script overwrites the staging environment with the latest production version and saves a backup before each operation.
- ⚠️ Security: Set permissions to 700 for the staging environment as well.
- 7.2 Cron jobs
To automate backups and the workflow, add the following lines to the root crontab (sudo crontab -e) :
# -------------------------------
# Daily PROD backup
# -------------------------------
0 3 * * * /var/www/pragma-cms/scripts/cron/backup.sh >/var/www/pragma-cms/storage/logs/cron_backup.log 2>&1
# -------------------------------
# Check for CMS updates
# -------------------------------
0 4,16 * * * sudo -u www-data /usr/bin/php /var/www/pragma-cms/scripts/cron/check_updates.php >> /var/www/pragma-cms/storage/logs/cron_update.log 2>&1
- The logs will be saved in
/var/log/pragma_backup.log
and/var/log/pragma_update.log. - ⚠️ Check that root has access to
/var/log
and that there is sufficient space.
- 7.3 Post-installation checks
Before considering the server ready for use:- Active services:
systemctl status php8.4-fpm
systemctl status caddy
systemctl status mariadb
systemctl status fail2ban
- PHP Test:
curl -s http://localhost/test.php | grep"PHP Version"
- Caddy/HTTPS Test :
- Check your domains (production and staging) via a web browser.
- Logs :
/var/log/caddy/platform-access.log
and/var/log/caddy/staging-access.log
- Backup test:
sudo /var/www/pragma-cms/scripts/cron/backup.sh
ls -lh /var/backups/pragma-cms
- Staging Test:
sudo /var/www/pragma-cms/scripts/cli/deploy_staging.sh
- Please check that the files and databases are properly synchronised.
- 7.4 Safety and maintenance notes
- Change the
pragma_masterpassword regularly if several people have access. - Check the Fail2Ban logs:
sudo fail2ban-client status sshd - Clear /var/log and
/root/backupsif disk space is running low. - Check OPcache: your
99-pragma.inimust be active (FPM must be restarted after each change).
- Change the