Storage Azure

ZenphotoCMS Photo Gallery and CMS on Ubuntu 24.04 on Azure User Guide

| Product: ZenphotoCMS on Ubuntu 24.04 LTS on Azure

Overview

ZenphotoCMS is a mature, open source photo gallery and lightweight content management system for publishing and organising images and videos online. It arranges media into a browsable album and subalbum tree, applies themes, reads EXIF and IPTC metadata, supports tags, categories and comments, and manages user accounts with per album access control. Its built in Zenpage CMS adds static pages plus a news and blog section, and a full backend lets you manage everything from one web interface. The cloudimg image ships ZenphotoCMS served by nginx and PHP 8.3 FPM on a hardened, fully patched Ubuntu 24.04 LTS base, together with a bundled local MariaDB server so the appliance is complete and useful on its own. The setup wizard has already been run and its scripts protected, so the platform is ready the moment the instance boots. The bundled MariaDB listens only on the loopback interface (127.0.0.1:3306). A unique administrator password is generated on the first boot of every VM. Backed by 24/7 cloudimg support.

What is included:

  • ZenphotoCMS served by nginx and PHP 8.3 FPM, managed by systemd
  • A bundled MariaDB server holding the Zenphoto database, already installed and ready
  • The setup wizard already run, its scripts protected, and the GPL licence pre accepted so the backend shows the sign in page directly
  • A per VM administrator password generated on first boot and recorded in a root only file
  • No shipped default login: the administrator account is created with a unique random password, and the shipped image carries that password rotated to a discarded random value
  • MariaDB bound to 127.0.0.1 only, never exposed to the network
  • Gallery media stored under /var/www/zenphoto/albums, with a small sample album so the gallery is populated on first view
  • nginx.service, php8.3-fpm.service and mariadb.service as enabled systemd units
  • An unauthenticated /healthz endpoint for Azure Load Balancer health probes
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a sensible starting point for a small gallery; step up to Standard_D2s_v3 for larger galleries or heavier traffic. NSG inbound: allow 22/tcp from your management network and 80/tcp for the application (add 443/tcp if you terminate TLS on the VM). The bundled MariaDB is never exposed: it listens on 127.0.0.1 only, so port 3306 stays off the network.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for ZenphotoCMS by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create then Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name zenphoto \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

Then open port 80:

az vm open-port --resource-group <your-rg> --name zenphoto --port 80

Step 3 - Confirm the services are running

SSH in as azureuser and confirm nginx, PHP FPM, MariaDB and the first boot service are all active. Note that MariaDB listens only on 127.0.0.1:3306 while nginx serves ZenphotoCMS on port 80.

systemctl is-active nginx php8.3-fpm mariadb zenphoto-firstboot
ss -tlnp | grep -E ':80 |:3306 ' | sed 's/  */ /g'

The nginx, php8.3-fpm, mariadb and zenphoto-firstboot services active, with nginx on port 80 and MariaDB bound to loopback 127.0.0.1:3306

Step 4 - Retrieve the per VM administrator password

Every VM generates its own ZenphotoCMS administrator password on first boot and writes it, along with the login user and the gallery and backend URLs, to a root only credentials file. Read it with sudo:

sudo cat /root/zenphoto-credentials.txt

The per VM ZenphotoCMS credentials file, showing the admin login user, the generated password masked, the gallery and backend URLs and the loopback MariaDB details

You sign in to the backend as user admin with the ZENPHOTO_ADMIN_PASSWORD from this file. On the box itself you can also run sudo mariadb to reach the database directly over the local socket without a password.

Step 5 - View the public gallery

Browse to http://<vm-public-ip>/. You land on the public photo gallery. A small cloudimg-sample album ships with the image so the gallery is populated the moment your instance boots.

The public ZenphotoCMS gallery showing the cloudimg-sample album thumbnail, ready to browse immediately after launch

Step 6 - Sign in to the backend

Browse to http://<vm-public-ip>/zp-core/admin.php. Enter user admin and the password from Step 4, then select the sign in button.

The ZenphotoCMS backend sign in page showing the user and password fields

After signing in you land on the admin Overview, which shows the installation information and the utility functions for the gallery, cache and database.

The ZenphotoCMS admin overview after signing in, showing the installation information and utility functions

Step 7 - Manage albums and images

Open the Albums tab to create albums and subalbums and to upload images and videos. Each album can carry a title, description, tags, a publish date and per album access control, and every image inherits the album's theme and metadata handling. Use the Upload tab to add media in bulk, and the Themes and Plugins tabs to change how the gallery looks and behaves.

The ZenphotoCMS Albums management view for creating albums and uploading images

Step 8 - No known or default credentials

The cloudimg image never ships a known administrator login. The administrator account is created with a unique random password at build time and rotated again to a fresh per VM random secret on the first boot of every VM; the shipped image carries the account password rotated to a discarded random value. You can prove the security posture on the box. Exactly one valid administrator exists, the setup environment is protected so it cannot be run by an attacker, and the built in round trip check confirms the per VM administrator signs in through the real backend login:

sudo mariadb -N -B zenphoto -e "SELECT id,user,valid FROM zp_administrators WHERE valid=1;"
sudo test -d /var/www/zenphoto/zp-core/setup && ls /var/www/zenphoto/zp-core/setup/*.php >/dev/null 2>&1 && echo 'setup runnable' || echo 'setup protected'
sudo bash /usr/local/sbin/zenphoto-cred-roundtrip.sh

The single valid admin row in the Zenphoto administrators table, the setup environment reported as protected, and the round trip check confirming the per VM admin authenticates via the backend login

Step 9 - Verify the stack and where your data lives

Confirm the health endpoint, the installed ZenphotoCMS version and where your gallery media lives. The document root at /var/www/zenphoto holds the application, the configuration file sits under /var/www/zenphoto/zp-data, and all gallery media is stored under /var/www/zenphoto/albums:

curl -sI http://127.0.0.1/healthz | head -1
grep ZENPHOTO_VERSION /var/www/zenphoto/zp-core/version.php | head -1
ls -ld /var/www/zenphoto/albums /var/www/zenphoto/zp-data

The healthz endpoint returning 200, the ZenphotoCMS version 1.6.8, and the albums and zp-data directories

Adding your own domain and TLS

The appliance serves plain HTTP on port 80. For production, point a DNS record at your VM and obtain a certificate with Let's Encrypt:

sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain>

ZenphotoCMS derives its base URL from the request Host header, so once you reach the VM on your domain over HTTPS the gallery links render correctly with no further change.

Backing up

Your gallery data is the Zenphoto database plus the albums content directory. Dump the database and archive the media tree:

sudo mysqldump zenphoto > /tmp/zenphoto-backup.sql
sudo tar czf /tmp/zenphoto-albums-backup.tar.gz -C /var/www/zenphoto albums

Copy both files off the VM on your usual schedule. To restore, load the SQL dump into the zenphoto database and unpack the archive back to /var/www/zenphoto/albums.

Security notes

  • ZenphotoCMS serves plain HTTP on port 80 out of the box. For anything beyond a trusted network, add your own domain and TLS as above, or front the VM with Azure Application Gateway.
  • The bundled MariaDB listens on 127.0.0.1 only and is never reachable from the network. Administer it through the Zenphoto backend or over SSH with sudo mariadb.
  • The administrator password is unique per VM and no known or blank credential authenticates. Keep /root/zenphoto-credentials.txt protected. To rotate the password, sign in to the backend, open the Users tab, edit the admin user and set a new password.
  • The setup environment is protected (its scripts are renamed so they cannot be executed) and nginx additionally denies /zp-core/setup, /zp-data and dotfiles, so the config, the database credentials and the setup tools are never web reachable.
  • Apply operating system updates regularly with sudo apt update && sudo apt upgrade; unattended security updates are enabled by default.