ZenphotoCMS Photo Gallery and CMS on Ubuntu 24.04 on Azure User Guide
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.1only, 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.serviceandmariadb.serviceas enabled systemd units- An unauthenticated
/healthzendpoint 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'

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

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.

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.

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

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.

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

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

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.1only and is never reachable from the network. Administer it through the Zenphoto backend or over SSH withsudo mariadb. - The administrator password is unique per VM and no known or blank credential authenticates. Keep
/root/zenphoto-credentials.txtprotected. To rotate the password, sign in to the backend, open the Users tab, edit theadminuser 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-dataand 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.