FlatPress Blog and CMS on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and configuration of FlatPress on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. FlatPress is a lightweight, flat-file blogging engine and content management system, published at github.com/flatpressblog/flatpress. It needs no database at all: every post, static page, comment, setting and account is stored as an ordinary file on disk, so there is nothing to install, secure, tune or back up beyond a folder of files.
The image installs FlatPress 1.5.1 served by nginx and PHP 8.3 FPM. Because the engine is entirely file based there is no database process anywhere in the image, which keeps the footprint tiny. Unattended security upgrades are configured to keep the server patched on your running VM.
One appliance, one web application. nginx and php8.3-fpm serve the whole site on port 80. There is no database and no mail engine — port 80 is the only network-reachable service. FlatPress stores all of its state as files under fp-content/, which the web server never serves.
Security by design — there is no administrator account in the image. FlatPress ships a web setup wizard that would let an unauthenticated visitor complete installation and seize the administrator account on a fresh site. This image closes that hole completely: the setup wizard is removed from the image entirely, and the image ships no account, no configuration and no secret at all. On the very first boot of every VM a one shot service generates a unique administrator password, completes installation non-interactively, creates the single administrator account and the per-install security salt, and then proves the new password signs in through the real login form and that admin, a blank password and other common guesses do not, before writing /root/flatpress-credentials.txt (mode 0600, root only).
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 the administrator account, configuration and salt are in place. Until that marker exists systemd skips those units entirely, so there is no window in which a half-provisioned or empty site is reachable. The units are still enabled, so the appliance comes straight back after a reboot.
What is included:
-
FlatPress 1.5.1 served by nginx and PHP 8.3 FPM, with a strict nginx allowlist that serves the theme, plugin and media assets but never the configuration, the account files or the per-install secret
-
A first-boot service that mints the per-instance administrator password, completes installation, and proves the credentials before the site is reachable
-
The bootstrap gate that stops nginx and php-fpm serving until provisioning is complete
Prerequisites
- An Azure subscription and either the Azure Portal or the Azure CLI (
az) signed in - An SSH key pair so you can read the generated credentials file
- Inbound TCP port 80 open to the browsers that will reach the site (and port 22 for administration)
Step 1: Deploy from the Azure Portal
- Open the cloudimg FlatPress Blog and CMS on Ubuntu 24.04 LTS offer in the Azure Marketplace and choose Create.
- Pick a resource group and region, keep the recommended Standard_B2s size (FlatPress is tiny and runs comfortably on 2 vCPU / 4 GB), and provide your SSH public key for the
azureuseraccount. - On the Networking step allow inbound 80 (the web application) and 22 (SSH). FlatPress serves everything over port 80; put it behind an Azure Application Gateway or a reverse proxy if you want TLS.
- Create the VM and note its public IP address.
Step 2: Deploy from the Azure CLI
RG="flatpress-prod"; LOCATION="eastus"; VM_NAME="flatpress"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/flatpress-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az vm create \
--resource-group "$RG" --name "$VM_NAME" \
--image "$GALLERY_IMAGE_ID" \
--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
Step 3: First boot and your credentials
On first boot the image mints the per-instance administrator password, completes the FlatPress installation, creates the administrator account and the per-install salt, opens the bootstrap gate, starts nginx and php-fpm, verifies the new credentials work and the common defaults are rejected, and writes /root/flatpress-credentials.txt. This completes within a minute or two. SSH in as azureuser and read the details:
sudo cat /root/flatpress-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 login URL and the administrator username (admin) and password.
Step 4: Confirm the appliance is running
Both services should report active. ss confirms the web application is on port 80 — and that there is no database or mail listener, because FlatPress needs neither.
systemctl is-active php8.3-fpm.service nginx.service
php -r 'echo "PHP ".PHP_VERSION."\n";'
nginx -v
ss -tulnp | grep ':80 ' | sed 's/users:.*//'

Step 5: View your blog
Browse to http://<your-vm-ip>/. FlatPress renders your blog immediately, with a seeded welcome post so the site is not empty. The theme, the sidebar menu, categories, archives and search are all wired up.

Step 6: Sign in to the admin panel
Browse to http://<your-vm-ip>/login.php. Sign in with the administrator username admin and the password from the credentials file.

Step 7: The administration area
Once signed in you land on the FlatPress administration area. From here you manage entries, static pages, the media uploader, sidebar widgets, plugins, themes and the site configuration.

Step 8: Write your first post
This is what FlatPress exists to do. Open Entries → Write Entry, give your post a subject and content with the built-in BBCode editor, optionally set categories, a description and keywords, then Publish. Your post appears on the blog immediately — stored as a plain file, with no database write.

Step 9: Change the administrator password
Open Configuration (or the Users area) and set a new administrator password. From then on use your own password; the generated one in the credentials file was only to get you in.
Step 10: Prove the per-VM administrator works and defaults are rejected
The image ships no shared credential. The credentials file is root-only, the account file is not world-readable, and the real login form accepts only the per-VM administrator password:
ls -l /root/flatpress-credentials.txt
stat -c 'credentials mode %a owner %U:%G' /root/flatpress-credentials.txt
stat -c 'admin account file mode %a owner %U:%G' /var/www/flatpress/fp-content/users/admin.php
# the per-VM administrator password signs in through the real login form:
sudo /usr/local/sbin/flatpress-login-check.sh admin '<FLATPRESS_ADMIN_PASSWORD>' \
&& echo "per-VM administrator: SIGNED IN" \
|| echo "per-VM administrator: run this on your VM (the password is substituted from the credentials file)"
# every guessable default is rejected:
for p in admin password flatpress 123456 ""; do
if sudo /usr/local/sbin/flatpress-login-check.sh admin "$p"; then
echo " '$p' ACCEPTED (unexpected)"
else
echo " '$p' rejected (correct)"
fi
done
# the account file and the per-install salt are never web-served:
curl -s -o /dev/null -w ' GET /fp-content/users/admin.php -> HTTP %{http_code} (404 = not served)\n' http://127.0.0.1/fp-content/users/admin.php
curl -s -o /dev/null -w ' GET /fp-content/config/hashsalt.conf.php -> HTTP %{http_code} (404 = not served)\n' http://127.0.0.1/fp-content/config/hashsalt.conf.php

Step 11: The flat-file model — no database
Everything FlatPress stores is a file under fp-content/. Your posts live under fp-content/content/, the configuration and per-install salt under fp-content/config/, and your account under fp-content/users/. There is no database engine anywhere on the machine.
ls /var/www/flatpress/fp-content/
sudo find /var/www/flatpress/fp-content/content -name 'entry*.txt' | head -3
systemctl list-units 'mariadb*' 'mysql*' 'postgresql*' --all --no-legend | head -3 || echo ' (no database units — FlatPress needs none)'
curl -s http://127.0.0.1/ | grep -o 'Welcome to your FlatPress blog' | head -1

Step 12: How the first-boot gate protects an unprovisioned instance
The distinguishing security property of this image is that the web tier cannot serve until provisioning is complete. Each web unit carries a ConditionPathExists drop-in on the bootstrap-ready marker, which first boot writes only after the administrator has been created — so there is never a moment when an empty site is reachable and a visitor could run the installer or become the first administrator.
for u in php8.3-fpm nginx; do
printf '%-12s %s\n' "$u" "$(grep -h ConditionPathExists /etc/systemd/system/$u.service.d/cloudimg-bootstrap-gate.conf)"
done
ls -l /var/lib/cloudimg/
systemctl is-enabled php8.3-fpm.service nginx.service
# the removed installer is not reachable:
curl -s -o /dev/null -w ' GET /setup.php -> HTTP %{http_code} (404 = installer removed)\n' http://127.0.0.1/setup.php

Step 13: Static pages, menus, comments and themes
From the admin area you can create standalone static pages (an About or Contact page), edit the sidebar menu, moderate reader comments with the built-in spam controls, arrange sidebar and footer widgets, and switch or restyle the theme. FlatPress ships with the Leggero theme and a set of plugins (BBCode editor, comments, search, archives, RSS) enabled out of the box.
Step 14: Back up and restore
Because FlatPress is entirely file based, a full backup is just a copy of the content directory — there is no database to dump:
sudo tar czf "flatpress-backup-$(date +%F).tar.gz" -C /var/www/flatpress fp-content
ls -lh flatpress-backup-*.tar.gz
Restore by extracting the archive back over fp-content on a fresh instance (after its first boot has run), preserving your posts, pages, comments, configuration and account.
Step 15: Security recommendations
- Put FlatPress behind TLS. The image serves plain HTTP on port 80. Terminate HTTPS at an Azure Application Gateway, a load balancer, or a reverse proxy in front of the VM, and restrict port 80 to that front end.
- Restrict SSH to your administration network with an NSG rule.
- Rotate the administrator password (Step 9) and keep the credentials file (
/root/flatpress-credentials.txt) readable only by root. - Keep the OS patched. Unattended security upgrades are enabled; reboot periodically to pick up new kernels.
- Back up regularly (Step 14) — it is only a folder of files.
Step 16: Support and licensing
FlatPress is distributed under the GNU General Public License, version 2 (GPL-2.0). This cloudimg image bundles FlatPress 1.5.1 with nginx and PHP from Ubuntu 24.04, configured and hardened as described above.
cloudimg provides 24/7 support for this image by email at support@cloudimg.co.uk. We help with initial setup, blog and page authoring, themes and styling, plugins, navigation menus and widgets, comment moderation and spam control, media uploads, file based backup and restore, and FlatPress version upgrades.
All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.