WonderCMS 3.6.0
Overview
WonderCMS is a tiny open-source content management system for simple websites. There is no database and no separate admin area: you sign in with one password, click any text on a page to edit it, and click outside it to save. Pages, menus, settings and the password hash live in a single JSON file, so the whole site is one folder that you can back up and move with ordinary file tools. Themes and plugins (a WYSIWYG editor, a blog, a contact form, statistics and more) install from the settings panel.
This guide covers launching and using the WonderCMS 3.6.0 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. The AMI is one version of the SecureAnyCloud BookStack, CMS, Blogs and Wikis listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.
What you get:
- No first-visit setup page, no default login. Out of the box, WonderCMS creates its data file on the first visit to the site, keeps the login page at the well-known address
/loginURLand prints the generated password on the public home page. On this image, your instance's first boot writes the data file before the web server starts. It sets an admin password and a private login address (/login-followed by 16 random hex characters), both generated on your instance and recorded in/root/wondercms-credentials.txt. The home page prints no password, and/loginURLis an ordinary "page not found". Nothing from the image build ships in the image. - HTTPS from the first boot. nginx serves WonderCMS on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
- Fail closed. nginx and PHP refuse to start until first boot has finished.
- The data file is never served. WonderCMS relies on Apache
.htaccessrules that nginx ignores, so this image replaces them. Thedatafolder (the JSON file with the password hash, caches, theme and plugin sources) is never served. The exception is uploaded files under/data/files/, which are served as static files with a policy that stops an uploaded HTML or SVG file from running script. PHP runs only throughindex.php, and theme, plugin and uploaded PHP files are never run directly. - Failed-login lockout. WonderCMS itself does not limit login attempts. On this image, ten failed sign-ins from one address within 15 minutes block that address for five minutes. The address comes from the connection, never from a header that a client sends.
- Hardened defaults. The WonderCMS core (
index.php) is owned by root and read-only to PHP. PHP can write only thedatafolder (themes and plugins install there), cannot run shell commands and is confined to WonderCMS's folders. nginx and PHP are blocked from the instance metadata service, and the AMI requires IMDSv2. Requests for a host name that the instance does not serve are dropped, and WonderCMS's links always use the host name you configure, never the one a request claims. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
- On the image: WonderCMS 3.6.0 (the official release, checksum verified) with its default Sky theme, PHP 8.3 FPM, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. First boot creates WonderCMS's standard starter site: a Home page and a How to page.
- Default user:
ubuntu - Architecture: x86_64
- Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route that you use for management).
- A security group that allows TCP 22 (SSH), TCP 443 (the site and its editor) and TCP 80 (redirects to HTTPS) from the networks that should reach them. A public website needs ports 443 and 80 open to everyone. Keep port 22 open to your own networks only.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick WonderCMS 3.6.0 on Ubuntu 24.04 as the software version and select your Region, then choose Launch through EC2.
Choose m5.large, your key pair and a security group as described in Prerequisites. WonderCMS is very light, so a smaller instance is fine for a small site.
To launch with the AWS CLI instead, replace the values in angle brackets:
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=wondercms}]'Connect to your instance
Connect over SSH. The default SSH user for WonderCMS 3.6.0 on Ubuntu 24.04 is ubuntu.
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot takes a few seconds. When it finishes, nginx, PHP and the first-boot unit are active:
systemctl is-active nginx php8.3-fpm wondercms-firstbootactive active activenginx serves ports 80 and 443, and PHP listens on a local socket only. The only open ports are 22, 80 and 443:
sudo ss -tlnp | awk 'NR==1 || /nginx|sshd|php/' | awk '{print $1, $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g'State Local Peer LISTEN 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx")) LISTEN 0.0.0.0:22 users:(("sshd"),("systemd")) LISTEN 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx")) LISTEN [::]:80 users:(("nginx"),("nginx"),("nginx")) LISTEN [::]:22 users:(("sshd"),("systemd")) LISTEN [::]:443 users:(("nginx"),("nginx"),("nginx"))grep -oE "define\('VERSION', '[0-9.]+'\)" /var/www/wondercms/index.php php -v | head -1define('VERSION', '3.6.0') PHP 8.3.6 (cli) (built: Oct 19 2026 15:16:58) (NTS)Get your login address and password
The credentials file holds the address that the site is served as (your instance's public DNS name by default), the private login address and the admin password generated on this instance. Show the file with:
sudo cat /root/wondercms-credentials.txtWONDERCMS_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ WONDERCMS_LOGIN_SLUG=login-<16 hex characters> WONDERCMS_LOGIN_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/login-<16 hex characters> WONDERCMS_ADMIN_PASSWORD=<24 letters and digits>To show the address lines alone, without the password, run:
sudo grep -E '^WONDERCMS_(URL|LOGIN_URL)=' /root/wondercms-credentials.txtWONDERCMS_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ WONDERCMS_LOGIN_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/login-<16 hex characters>Treat the login address as private. The site does not link to it, and anyone who knows it still needs the password.
Visit your site and sign in
Open WONDERCMS_URL in your browser. The certificate is self-signed (generated on your instance), so accept the browser warning once, or install your own certificate (Step 10). Visitors see the starter site in the Sky theme, with no login link and no password:
Open WONDERCMS_LOGIN_URL and sign in with WONDERCMS_ADMIN_PASSWORD. WonderCMS has one admin and no user name:
Edit your pages
When you are signed in, every editable area of the page has a dashed outline. Click any text to edit it in place, and click outside it to save. Changes are live immediately. Settings opens the settings panel, and the red button signs you out.
To add a page, open Settings > Menu and add a menu item. Alternatively, while signed in, browse to a new address such as /about and start typing. WonderCMS creates the page when you save.
Settings, themes and plugins
The settings panel has tabs for the Current page (title, keywords and description for search engines), the Menu, Files (upload images and documents, served from /data/files/), Themes, Plugins and Security (change the login address and the password).
Themes and Plugins list the modules from WonderCMS's online catalogue. Install downloads the module from its GitHub repository into the data folder. For example, the Summernote editor plugin adds a WYSIWYG toolbar to every editable area.
A theme or plugin is PHP code that runs inside your site, so install only modules that you trust. Only the signed-in admin can install them. The instance needs outbound HTTPS to GitHub for the catalogue and for installs. PHP cannot reach the instance metadata service, so a module cannot read the instance's IAM role credentials.
Your site data
Everything WonderCMS stores lives in one folder: the JSON data file (pages, menu, settings and the password hash), uploaded files, and the installed themes and plugins.
sudo ls -l /var/www/wondercms /var/www/wondercms/data | sed -E 's/ +/ /g' | cut -d' ' -f1,3,4,9-/var/www/wondercms: total drwxr-x--- www-data www-data data -rw-r--r-- root root index.php lrwxrwxrwx root root plugins -> data/plugins lrwxrwxrwx root root themes -> data/themes /var/www/wondercms/data: total -rw-r----- www-data www-data database.js drwxr-xr-x www-data www-data files drwxr-xr-x www-data www-data plugins -rw-r----- www-data www-data security.json drwxr-xr-x www-data www-data themesAfter your first sign-in, WonderCMS also keeps cache.json there (the module catalogue), and the login lockout keeps its counts in sac-login-guard.json.
Back up the folder by copying it (sudo tar -C /var/www/wondercms -czf wondercms-data.tar.gz data) or with an EBS snapshot of the instance's volume. WonderCMS's own Settings > Security > Backup website button writes a zip of the whole site into /data/files/ and downloads it. Delete the zip from Files afterwards, because anyone who knows the file name can download anything in /data/files/.
Security checks
Port 80 sends browsers to HTTPS, and the health endpoint answers load balancer checks. The data file, the default login address and theme PHP files are not reachable:
H=$(sudo grep '^WONDERCMS_URL=' /root/wondercms-credentials.txt | sed -E 's#^WONDERCMS_URL=https://([^/]+)/.*#\1#')
curl -s -o /dev/null -w 'port 80: %{http_code} -> %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/how-to"
curl -sk --resolve "$H:443:127.0.0.1" "https://$H/healthz" for p in /data/database.js /data/security.json /loginURL /themes/sky/theme.php; do
curl -sk -o /dev/null -w "$p: %{http_code}\n" --resolve "$H:443:127.0.0.1" "https://$H$p" done
curl -s -o /dev/null -w 'unknown host name: %{http_code}\n' -H 'Host: example.com' http://127.0.0.1/ || trueport 80: 301 -> https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/how-to ok /data/database.js: 404 /data/security.json: 404 /loginURL: 404 /themes/sky/theme.php: 404 unknown host name: 000The status 000 means nginx closed the connection without an answer. The login address is your instance's own (not the default), the password is stored only as a PHP password hash, and WonderCMS builds HTTPS links:
sudo -u www-data php /usr/local/lib/sac/wondercms/wondercms-admin.php summarytheme=sky forceHttps=true loginIsDefault=no hashAlgo=bcrypt lastLogins=0 guard=0Only root can read the credentials file, only the web server can read the data file, and root owns the code:
sudo stat -c '%a %U:%G %n' /root/wondercms-credentials.txt /var/www/wondercms/data/database.js /var/www/wondercms/index.php600 root:root /root/wondercms-credentials.txt 640 www-data:www-data /var/www/wondercms/data/database.js 644 root:root /var/www/wondercms/index.phpThe certificate was generated on this instance and names its own addresses:
H=$(sudo grep '^WONDERCMS_URL=' /root/wondercms-credentials.txt | sed -E 's#^WONDERCMS_URL=https://([^/]+)/.*#\1#')
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltNamesubject=CN = ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhostnginx and PHP cannot reach the instance metadata service. PHP cannot run shell commands, is confined to WonderCMS's folders and runs the login guard before every request. nginx hands PHP the configured host name and the real client address from the connection, never values that a client sends:
systemctl show nginx -p IPAddressDeny
systemctl show php8.3-fpm -p IPAddressDeny php-fpm8.3 -i 2>/dev/null | grep -E '^(open_basedir|disable_functions|expose_php|auto_prepend_file) ' | cut -d' ' -f1-3 grep -E 'fastcgi_param (HTTP_HOST|HTTP_X_FORWARDED_FOR|HTTP_CLIENT_IP)' /etc/nginx/sites-available/sac-wondercmsIPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 auto_prepend_file => /usr/local/lib/sac/wondercms/login-guard.php disable_functions => exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec expose_php => Off open_basedir => /var/www/wondercms/:/usr/local/lib/sac/wondercms/:/var/lib/php/sessions/:/tmp/ fastcgi_param HTTP_HOST $wcms_host; fastcgi_param HTTP_X_FORWARDED_FOR $remote_addr; fastcgi_param HTTP_CLIENT_IP $remote_addr;If you lock yourself out with ten wrong passwords, wait five minutes or clear the lockout with:
sudo wondercms-reset-lockoutYour own DNS name and certificate
Point a DNS record at the instance, then set the name that WonderCMS is served as. This command creates a new self-signed certificate for the name. It also updates nginx (which hands the name to WonderCMS for every link) and the URLs in /root/wondercms-credentials.txt, and it keeps the name across reboots:
sudo wondercms-set-hostname <your-domain>To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx. Running wondercms-set-hostname again replaces it with a self-signed certificate:
sudo install -m 0644 <your-domain>.crt /etc/nginx/ssl/wondercms.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/ssl/wondercms.key
sudo nginx -t && sudo systemctl reload nginxOnce browsers trust the certificate you can also add an HSTS header (add_header Strict-Transport-Security "max-age=31536000" always;) to the HTTPS server in /etc/nginx/sites-available/sac-wondercms. The image does not send one: browsers ignore HSTS from a site whose certificate they do not trust, so it only takes effect with a trusted certificate.
Password, login address and upgrades
wondercms-set-password gives the admin a new random password, signs out every open admin session and records the password in /root/wondercms-credentials.txt. Run it without arguments, or pass your own password (24 to 64 letters and digits) on stdin. You can also change the password in Settings > Security.
sudo wondercms-set-password printf '%s\n' '<your-new-password>' | sudo wondercms-set-password --stdinTo move the login page, change the login URL in Settings > Security. WonderCMS uses the new address at once. Note it down, because the credentials file keeps the first-boot address.
The WonderCMS core is read-only to PHP, so WonderCMS's built-in core Update button cannot replace index.php on this image. Themes and plugins still update from the settings panel.
To move to a newer WonderCMS release later, snapshot the instance, download the release zip from wondercms.com or GitHub, and check its published SHA-256. Then replace /var/www/wondercms/index.php with the one from the zip (sudo install -o root -g root -m 0644 index.php /var/www/wondercms/index.php). Your site is untouched, because it lives in the data folder. The image ships the version named in the listing.
Server components
The image uses these components and paths:
- WonderCMS core (root-owned): /var/www/wondercms/index.php
- Site data (data file, uploads, themes, plugins): /var/www/wondercms/data (themes and plugins point into it)
- nginx site: /etc/nginx/sites-available/sac-wondercms
- PHP settings: /etc/php/8.3/fpm/conf.d/99-sac-wondercms.ini
- Login guard: /usr/local/lib/sac/wondercms/login-guard.php
- Certificate and key: /etc/nginx/ssl/
- First-boot script: /usr/local/sbin/wondercms-firstboot.sh
- Helpers: /usr/local/sbin/wondercms-set-hostname, /usr/local/sbin/wondercms-set-password, /usr/local/sbin/wondercms-reset-lockout
- Credentials: /root/wondercms-credentials.txt (mode 0600)
Security recommendations
- Allow port 22 in the security group from your own networks only.
- Keep the admin password and the login address secret, and rotate the password if it may have been shared (Step 11).
- Use your own DNS name and a certificate that your browsers trust (Step 10).
- Install only themes and plugins that you trust, and delete backup zips from Files once you have downloaded them.
- Keep the operating system patched. Unattended security upgrades are on, and
sudo apt-get update && sudo apt-get upgradeapplies everything else. - Back up the
datafolder regularly (Step 8).
Support and licensing
WonderCMS is open source under the MIT licence (a copy is at /usr/share/doc/wondercms/LICENSE); there is no per-user or per-CPU licence fee. The AMI price covers SecureAnyCloud's hardening, first-boot setup and support.
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
- Support: 24/7, with a 24-hour response SLA
Deploy on AWS
Launch WonderCMS 3.6.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com