Deployment guide

Evolution CMS

Overview

Evolution CMS is an open source PHP content management system and application framework. The Evolution CMS community develops it and publishes it at evo.im. It descends from the long-standing MODX Evolution lineage and is still under active development.

Evolution CMS imposes no fixed structure on a site. Pages are called resources, and the developer composes everything a template needs: reusable snippets and chunks, template variables and a flexible tag-based templating language. A full-featured Manager admin dashboard gives a content team resource editing, a file manager, user roles and permissions, and a plugin and module system for extending the CMS without touching core code.

This guide covers the deployment and configuration of Evolution CMS on Ubuntu 24.04 on Azure using SecureAnyCloud Azure Marketplace images. The image installs Evolution CMS 3.5.7 from a SecureAnyCloud-mirrored copy of the official tagged release, nginx and PHP 8.3 FPM to serve it, and MariaDB from Ubuntu 24.04 to hold the site data, and it wires all three together. Unattended security upgrades keep the server patched on your running VM.

One appliance, three services. nginx and php8.3-fpm serve the site on port 80, and mariadb holds every resource, account and setting. MariaDB is bound to 127.0.0.1:3306 and PHP FPM listens on a Unix socket, so neither is ever exposed to the network. Port 80 is the only reachable surface.

An allowlist nginx configuration, not a single web root. Unlike a modern framework with a public/ subdirectory, Evolution CMS serves directly from its own tree root, so the nginx configuration is an explicit allowlist. nginx denies core/ (the application code, its vendored dependencies, and the database configuration file that holds this VM's database password) and install/ outright, regardless of file type, even though both live inside the web root on disk. nginx serves assets/ (the file manager's upload, cache and backup trees) as static content only, and it refuses PHP execution there, so an uploaded .php file can never run. /manager/ is the one PHP-executing area outside the public front controller. It is Evolution CMS's own admin application, reached exactly as its own routing expects.

Security by design: there is no administrator account in the image. Rather than shipping a default login and rotating it, this image ships no database schema, no administrator account and no install wizard reachable at any point. On the first boot of every VM, a one-shot service runs Evolution CMS's own non-interactive installer. The installer creates the database schema and the single administrator account with a unique per-VM password. The service then proves that the new password signs in through the real Manager login form and that admin/admin, a blank password and other common guesses do not. Only then does it delete the install directory from disk and open the web server. Until every one of those steps has completed, nothing is listening on port 80 at all.

The site cannot serve an unprovisioned instance. nginx and php8.3-fpm are each gated on a bootstrap marker that first boot writes only after every credential is in place and the install wizard is gone. Until that marker exists, systemd skips those units entirely, so there is no window in which a half-provisioned site or the setup wizard is reachable. The units are still enabled, so the site comes straight back after a reboot.

What you get:

  • Evolution CMS 3.5.7 served by nginx and PHP 8.3 FPM, with the repository root as the web root and core/, install/ and PHP execution inside assets/ all denied at the web server.
  • MariaDB (mariadb.service) holding the site data, bound to loopback.
  • A full-featured Manager admin dashboard with resource editing, a file manager, user roles and permissions, and a plugin and module system.
  • A per-instance database password and administrator password generated on first boot and documented in /root/evolution-cms-credentials.txt (0600).
  • An install wizard that is deleted from disk in the same first-boot step that creates your administrator account, so it can never be reached again.

Prerequisites

  • An active Azure subscription, an SSH public key, and a VNet and subnet in the target region.
  • A subscription to the Evolution CMS listing on Azure Marketplace.
  • Network Security Group rules that allow TCP 22 (administration) and TCP 80 (the website) from the networks that need them.
  • A registered domain, plus access to your DNS provider, if you want the site to be reachable by name rather than by IP address.
1

Deploy from the Azure Portal

Search for Evolution CMS in the Marketplace, select the SecureAnyCloud publisher, and choose Create. Choose Standard_B2s or larger.

Configure the Network Security Group to allow TCP 80 for the website from the internet, or from your own networks while you set it up. Allow TCP 22 for administration from your administrative networks only. The database and the PHP runtime stay on loopback and are never exposed.

2

Deploy from the Azure CLI

RG="content-prod"; LOCATION="eastus"; VM_NAME="evolution-cms" IMAGE_URN="sac1647283583153:evolution-cms:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION"
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:evolution-cms:default:latest
az vm create \
  --resource-group "$RG" --name "$VM_NAME" \
  --image "$IMAGE_URN" \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values "$SSH_KEY" \
  --public-ip-sku Standard
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 80 --priority 1001
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 22 --priority 1002
3

First boot and your credentials

On first boot, the image generates this VM's database password and administrator password. It creates the database schema and the single administrator account, opens the bootstrap gate and starts the web server. It then verifies that the new credentials work and that the common defaults are rejected, deletes the install directory, and writes /root/evolution-cms-credentials.txt. This completes within a minute or two.

SSH in as azureuser and read the details:

sudo cat /root/evolution-cms-credentials.txt

The file is mode 0600 and owned by root, so only a privileged user can read it. It contains the site URL, the administrator username and password, and the database name, user and password.

Confirm that the install wizard and the database configuration file are both unreachable, as they are on every fresh instance:

for p in /install /install/index.php /install/cli-install.php \
  /core/config/database/connections/default.php; do curl -s -o /dev/null -w "%{http_code} $p\n" "http://127.0.0.1$p" done

Then confirm the credentials file, every enabled unit and the absence of swap on the OS disk:

sudo stat -c '%a %U:%G %n' /root/evolution-cms-credentials.txt systemctl is-enabled mariadb.service php8.3-fpm.service nginx.service evolution-cms-firstboot.service swapon --show
4

Confirm the appliance is running

All three services should report active. The ss output confirms that the site is on port 80 and that MariaDB is bound to loopback only. The response headers carry no server version banner.

systemctl is-active mariadb.service php8.3-fpm.service nginx.service php -r 'echo "PHP ".PHP_VERSION."\n";' mysql --version nginx -v ss -tlnp | grep -E ':(80|3306) ' | sed 's/users:.*//' | sort curl -sI http://127.0.0.1/ | head -4

Expected output:

active active active PHP 8.3.6 mysql Ver 15.1 Distrib 10.11.14-MariaDB, for debian-linux-gnu (x86_64) using EditLine wrapper nginx version: nginx/1.24.0 (Ubuntu) LISTEN 0 511 [::]:80 [::]:* LISTEN 0 511 0.0.0.0:80 0.0.0.0:* LISTEN 0 80 127.0.0.1:3306 0.0.0.0:* HTTP/1.1 200 OK Server: nginx Date: ... Content-Type: text/html; charset=UTF-8
5

Sign in to the Manager

Browse to http://<your-vm-public-ip>/manager/. The Evolution CMS Manager login page opens.

Sign in with the username admin and the password from the credentials file. On a new install, the Manager first shows a System configuration review screen. This is expected on first login only. Review or accept the defaults, then choose Save + Continue editing to proceed into the Manager.

You can prove the same sign-in from the command line. These commands use the per-instance password from the credentials file to complete a real login through the real Manager form, then check that common default passwords are refused:

sudo /usr/local/sbin/evolution-cms-login-check.sh admin '<EVOLUTION_ADMIN_PASSWORD>' && echo "per-VM password: ACCEPTED (correct)"
sudo /usr/local/sbin/evolution-cms-login-check.sh admin 'admin' || echo "admin/admin: REJECTED (correct)"
sudo /usr/local/sbin/evolution-cms-login-check.sh admin '' || echo "blank password: REJECTED (correct)"

Expected output:

per-VM password: ACCEPTED (correct) admin/admin: REJECTED (correct) blank password: REJECTED (correct)

You can also run the full credential round trip that SecureAnyCloud itself uses as a build gate. It never prints a credential value. It reports only pass or fail against the real login form:

sudo /usr/local/sbin/evolution-cms-cred-roundtrip.sh

After the first-login settings review, the Manager shows the resource tree down the left and the details of the selected resource on the right. The seeded Evolution CMS Install Success resource ships with every fresh install.

6

Change the administrator password

After you sign in, set your own password first. Choose admin at the top right of the Manager, choose My Settings, and use the password fields to set a new password.

Evolution CMS stores passwords using a salted, iterated hash (the same phpass lineage WordPress uses), so the new password is never recoverable from the database. After you change the password, the copy in /root/evolution-cms-credentials.txt is stale. Keep the file for the database password, or remove the administrator password line from it.

7

Create your first resource

In the Manager's left-hand resource tree, choose the New document icon in the toolbar, or right-click the site root and choose Create Resource here. The New Resource form opens. Give the resource a Title, and write the page body into the Resource content field.

The pagetitle field is metadata used for the browser title and the Manager's own listings. A fresh page has no template assigned, which matches the seeded homepage. In that case the page renders the raw Resource content field with no wrapper at all. If you want the title to appear on the page itself, include it in the content, for example as an <h1> heading, exactly as in a normal HTML page.

Choose Save + Continue editing, then open the public site at the resource's URL. Use the Preview button on the Manager's toolbar, or browse to http://<your-vm-public-ip>/index.php?id=<resource-id>. The content you wrote in the Manager is now live on the public site. This is the core of what Evolution CMS does: it turns what you write in the dashboard into a published page.

Confirm from the command line that a published resource is public, with no session or cookie involved:

curl -s -o /dev/null -w 'home page: HTTP %{http_code}\n' http://127.0.0.1/
curl -s -o /dev/null -w 'healthz: HTTP %{http_code}\n' http://127.0.0.1/healthz

A freshly deployed instance answers 200 for both. The home page is the seeded resource, and /healthz is an unauthenticated liveness probe that nginx serves directly. It is suitable for an Azure Load Balancer health check.

A note on URLs. This image ships with Evolution CMS's friendly (alias-path) URLs turned off, so you reach every resource by its numeric ID (?id=N) rather than by an alias path. This is a deliberate, fully supported upstream configuration. Friendly URLs depend on a file-based alias cache that reflects a newly created resource only after the next cache rebuild. ?id=N links resolve directly and reliably regardless of the cache's state, which is the right trade-off for a freshly provisioned appliance. Evolution CMS's own link generator uses the same ?id=N form throughout the Manager and any auto-generated navigation.

8

Build out your template

A production site normally starts from a Template rather than leaving each resource blank. To create one, open Elements > Manage Templates in the Manager. A template is HTML with tags such as [*pagetitle*] for the resource's title and [*content*] for its body, plus any Template Variables (custom fields) you define under Elements > Template Variables.

When a template exists, assign it to a resource from the Settings tab of that resource's edit form. The page then renders through the template instead of as raw content.

Chunks (reusable HTML snippets, Elements → Manage Chunks) and Snippets (small PHP scripts that output content, Elements → Manage Snippets) let you build a header, footer, navigation menu or any other reusable piece once. You then reference it from every template with a tag such as {{header}} or [[MySnippet]].

9

Manage users, roles and the file manager

Users > Manage Users lists every Manager account and lets you create more, which is useful once you have more than one editor. Users > Manage Roles defines what each role can do inside the Manager, so you can give a content editor access to resources without exposing system settings or user management.

The built-in file manager lets you browse, upload and organise files under assets/. Open it from the folder icon in the toolbar or from Elements → Manage Files. This is the tree that the image already allows the web server to serve as static content. Anything you upload there is immediately reachable at its own URL, and PHP execution is refused as defence in depth.

10

Take a database backup

Evolution CMS's own assets/backup/ tooling, reachable from the Manager, can export the database. You can also take a full backup from the command line at any time:

CREDS=/root/evolution-cms-credentials.txt
DB_PASS=$(sudo sed -n 's/^evolution.db.password=//p' "$CREDS")
sudo mysqldump -u evolution -p"$DB_PASS" evolution_cms > "evolution-cms-backup-$(date +%Y%m%d).sql"

Store the resulting file outside the VM, for example in Azure Blob Storage or copied off with scp, so that the database backup survives even if you lose the VM.

11

Use your own domain and add TLS

Point an A record for your domain at the VM's public IP address. Evolution CMS resolves the site's own address dynamically from the incoming request, so the application needs no configuration change for the domain to start working once DNS propagates.

For TLS, the simplest option on a single VM is a reverse proxy in front of nginx, for example Caddy or an additional nginx server block with Certbot. The proxy terminates HTTPS on 443 and forwards to the existing site on 127.0.0.1:80. If you use Let's Encrypt, keep port 80 open for the ACME HTTP-01 challenge.

12

Keep the system patched

The base image ships with unattended-upgrades enabled for OS-level security patches. You can update Evolution CMS itself from the Manager (Dashboard > check for updates, where available) or by following the release notes at github.com/evolution-cms/evolution. Always take a database and filesystem backup first, as upstream's release notes recommend.

Troubleshooting

The Manager shows a 404 instead of the login page. manager/index.php refuses any request with no Accept-Language header. This affects only scripted curl checks without that header, because every real browser sends one automatically.

You forgot the administrator password. SSH in and read /root/evolution-cms-credentials.txt again. The password there is stale only if you have already changed it from the Manager. If you changed it and lost the new password, reset it directly in the database using Evolution CMS's own password hashing (EvolutionCMS\Legacy\PasswordHash) via sudo -u www-data php from /var/www/evolution. Alternatively, restore from a backup taken before the change.

A resource returns 404 after you create it. Confirm that it is Published (the publish checkbox on the resource's edit form) and that you are using its numeric ID (?id=N). See the note on friendly URLs in Step 7.

You need support. SecureAnyCloud provides 24/7 technical support for this image. See the Support tab on the Azure Marketplace listing for contact details.

Deploy on Azure

Launch Evolution CMS 3.5.7 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure