Gaming Azure

Azuriom Game CMS on Ubuntu 24.04 on Azure User Guide

| Product: Azuriom 1.2.12 Game CMS on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of Azuriom on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Azuriom is an open source content management system written specifically for game server communities, developed by the Azuriom team and published at azuriom.com. It gives a Minecraft, FiveM, Rust, ARK, Garry's Mod or Counter-Strike community a real website instead of a forum thread: a news feed with comments and likes, unlimited custom pages, a player account system with roles and permissions, live game server status, and an administration dashboard a community manager can run without touching a configuration file.

The image installs Azuriom 1.2.12 from the official upstream release archive, nginx and PHP 8.3 FPM to serve it, and MariaDB from Ubuntu 24.04 to hold the site data, and wires all three together. Unattended security upgrades are configured to 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 post, page, 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.

A Laravel deployment done properly. Azuriom is a Laravel application, and the document root is the framework's public/ directory and nothing above it. The application source, its vendored dependencies, the database migrations and above all .env — which holds this VM's application key and database password — live outside any URL the web server can address. On top of that, only the framework front controller is permitted to execute PHP, so a file placed anywhere else in the web root is refused rather than run.

Security by design — there is no administrator account in the image. Rather than shipping a default login and rotating it, this image ships no account, no .env, no application key and no database schema at all. On the very first boot of every VM a one shot service generates a unique Laravel APP_KEY, a unique database password and a unique administrator password, creates the database schema, creates the single administrator account with a bcrypt password hash, and then proves the new password signs in through the real login form and that admin/admin, a blank password and other common guesses do not, before writing /root/azuriom-credentials.txt (mode 0600, root only).

Why the per instance application key matters. APP_KEY is Laravel's session, cookie and encryption signing material. If it were baked into the image, every customer of that image would share it, and anyone holding the image could forge a signed session for anyone else's site. Here it is generated on your VM, for your VM. It also closes Azuriom's own setup wizard: upstream only opens the /install/* routes while APP_KEY is empty, so once first boot has written a real key the wizard is permanently shut and cannot be used by a passing visitor to seize your site.

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. 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 is included:

  • Azuriom 1.2.12 served by nginx and PHP 8.3 FPM, with the Laravel public/ directory as the only web root

  • MariaDB (mariadb.service) holding the site data, bound to loopback

  • A news system with comments, likes and attachments, unlimited custom pages with a rich editor, and a navigation editor

  • A player account system with registration, email verification, two factor authentication, password reset, roles and fine grained permissions

  • Live game server status and player counts, an action log of administrative changes, image and file management, and multi language support

  • A per instance application key, database password and administrator password generated on first boot and documented in /root/azuriom-credentials.txt (0600)

  • Azuriom core only. No third party theme or plugin is bundled, so no unverified extension licence travels in the image — the theme and extension marketplace is available from the dashboard when you want it

Prerequisites

  • Active Azure subscription, SSH public key, VNet and subnet in the target region

  • Subscription to the Azuriom listing on Azure Marketplace

  • Network Security Group rules allowing TCP 22 (administration) and TCP 80 (the website) from the networks that need them

  • A registered domain if you want the community to reach the site by name rather than by IP address, plus access to your DNS provider

Step 1: Deploy from the Azure Portal

Search Azuriom in Marketplace, select the cloudimg publisher, and click 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) and TCP 22 for administration from your administrative networks only. The database and the PHP runtime stay on loopback and are never exposed.

Step 2: Deploy from the Azure CLI

RG="community-prod"; LOCATION="eastus"; VM_NAME="azuriom"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/azuriom-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 generates this VM's application key, database password and administrator password, creates the database schema and the single administrator account, opens the bootstrap gate, starts the web server, verifies the new credentials work and the common defaults are rejected, and writes /root/azuriom-credentials.txt. This completes within a minute or two. SSH in as azureuser and read the details:

sudo cat /root/azuriom-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, email address and password, and the database name, user and password.

Step 4: Confirm the appliance is running

All three services should report active. ss confirms the site is on port 80 while MariaDB is bound to loopback only, and 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

All three services report active, PHP 8.3 MariaDB and nginx report their versions, ss shows the website on port 80 while MariaDB is bound to 127.0.0.1 port 3306 only, and the response headers carry no version banner

Step 5: Sign in to your site

Browse to http://<your-vm-public-ip>/ and you will see the Azuriom community home page with the stock theme. Click Login, or go straight to http://<your-vm-public-ip>/user/login, and sign in with the username admin and the password from the credentials file.

The Azuriom sign in page with the username or email field, the password field and a link to register an account

Once signed in, open http://<your-vm-public-ip>/admin for the administration dashboard. It shows counts of users, posts, pages and images, recent and active user charts, and the whole navigation for settings, content, users and extensions down the left. The sidebar header reports the game the site is configured for and the Azuriom version.

The Azuriom administration dashboard showing the version in the sidebar, counts of users posts pages and images, recent user and active user charts, and navigation for settings, navbar, servers, users, roles, bans, pages, posts, images and redirections

You can prove the same sign in from the command line. This uses the per instance password from the credentials file to complete a real login through the form, then checks that common default passwords are refused:

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

The credentials file listing proving mode 600 owned by root root, the application key confirmed set, decoding to 32 bytes and not the upstream default, the real login form rejecting admin/admin, admin/password, admin/azuriom, admin/123456 and a blank password, and exactly one account existing with an administrator role

Step 6: Change the administrator password

The first thing to do after signing in is set your own password. Click your avatar in the top right of the site and choose Profile, or browse to /profile, then use the Change password form. Enter the per instance password from the credentials file as the current password and choose a new one.

Azuriom stores passwords as bcrypt hashes, so the new password is never recoverable from the database. Once you have changed it, the copy in /root/azuriom-credentials.txt is stale — keep the file for the database password, or remove the administrator password line from it.

Step 7: Make the site your own

Open Settings in the admin sidebar. The General page sets the site name, description, keywords, timezone, default language and the copyright line in the footer. Set the site name first, because it appears in the navigation bar, the browser title and every email the site sends.

The Navbar page builds your navigation: add links to pages, categories, posts or external URLs, and drag them into the order and nesting you want. The Theme page lists the installed themes and lets you install more from the Azuriom marketplace without leaving the dashboard; the stock theme is fully usable and is what the screenshots in this guide show.

Under Content you will find Pages for static content such as rules, staff lists or a vote page, and Images for the media library that the editor draws on.

Step 8: Publish your first news post

Content → Posts → Create opens the post editor. Give the post a title and a short description, let the slug fill itself in from the title, then write the body in the rich text editor. The Published at field controls when it appears, and the pin switch keeps an announcement at the top of the news list.

The Azuriom create post form with the title, description, image, slug and published date fields, and the rich text editor holding the body of a Season 12 launch announcement

Click Save, then open the public site. The post appears in the News section of the home page as a card with its title, an excerpt of the body and a Read more button — this is the core of what Azuriom does, turning what you write in the dashboard into the community's public website.

The public Azuriom home page News section showing the Season 12 launches this Friday card with the post excerpt, a Read more button, the posted date and author, and the Powered by Azuriom footer

Following Read more takes any visitor — signed in or not — to the post's own page, with the full body, a like button and the comment thread.

An anonymous visitor reading the published Season 12 post on its own page, with the full body text, the likes button, the posted date and author, and a notice that you must be logged in to leave a comment

You can confirm from the command line that a published post is genuinely public, with no session or cookie involved:

curl -s -o /dev/null -w 'public home page: HTTP %{http_code}\n' http://127.0.0.1/
POSTS="$(curl -s http://127.0.0.1/ | grep -c 'Read more' || true)"
echo "published posts visible to an anonymous visitor: ${POSTS}"

A freshly deployed instance reports 0 here because you have not published anything yet; after Step 8 it reports the number of posts on the home page.

Step 9: Add your game server

Settings → Servers → Add registers a game server so the site can show its status and player count. Choose the server type, give it a name, and enter the host and query port. Azuriom pings the server and renders the result in the theme, so visitors can see at a glance whether the server is up and how busy it is.

The game the site is built around is set by the AZURIOM_GAME value in /var/www/azuriom/.env, which this image sets to Minecraft offline mode. To build the site around a different game, install that game's extension from Extensions in the dashboard and follow its instructions. After any manual change to .env, clear the cached configuration:

sudo -u www-data php /var/www/azuriom/artisan optimize:clear

Step 10: Configure mail so registration and password reset work

A fresh instance has no mail transport configured, so account verification and password reset emails are written to the application log rather than sent. Open Settings → Mail in the dashboard and enter your SMTP host, port, encryption, username and password, then send the test message from that page.

Mail settings are stored in the database, so you do not need to edit any file on disk. If you would rather configure the transport in the environment file, edit the MAIL_ values in /var/www/azuriom/.env and clear the cache as in the previous step. Keep the file's ownership and permissions as they are — it is root:www-data mode 0640 on purpose, because it holds this VM's application key.

Step 11: Open registration to your community

Registration is enabled by default so players can create accounts. Settings → General controls whether new registrations are accepted, and Users → Roles defines what each role can do — create the roles you need (member, moderator, staff) and grant each one only the permissions it should have. Users → Bans handles moderation.

Two factor authentication is available to every account from Profile → 2FA; enable it on your administrator account once you have set your own password.

Step 12: Use your own domain and add TLS

Point an A record for your domain at the VM's public IP address, then set the site address so links and emails use the name rather than the IP:

sudo sed -i 's|^APP_URL=.*|APP_URL="https://<your-domain>"|' /var/www/azuriom/.env
sudo -u www-data php /var/www/azuriom/artisan optimize:clear

Then obtain a certificate. Certbot's nginx plugin edits the existing site in place and leaves the allowlist intact:

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain> --redirect --agree-tos -m admin@<your-domain> --non-interactive

The dashboard shows a warning while the site is served over plain HTTP; it clears once the certificate is in place and HTTP redirects to HTTPS.

Step 13: How the security model works

Two mechanisms do the work, and both are verifiable on your own VM.

The bootstrap gate. nginx and php8.3-fpm each carry a systemd drop-in with ConditionPathExists pointing at a marker that first boot writes only after the application key, the database password and the administrator account all exist. Until then systemd skips the unit — it does not start and fail, it does not start at all — so an unprovisioned site is never reachable. Both units remain enabled so the site returns after a reboot.

grep -h ConditionPathExists /etc/systemd/system/nginx.service.d/cloudimg-bootstrap-gate.conf /etc/systemd/system/php8.3-fpm.service.d/cloudimg-bootstrap-gate.conf
ls -l /var/lib/cloudimg/
systemctl is-enabled php8.3-fpm.service nginx.service azuriom-firstboot.service

The nginx bootstrap gate drop-in showing ConditionPathExists on the bootstrap ready marker, the same condition on the php-fpm unit, the marker and first boot sentinel present in /var/lib/cloudimg, and all three units reporting enabled

The web root and the closed installer. Only public/ is served, so .env, config/, vendor/, artisan and the logs are not addressable at all, and Azuriom's setup wizard is shut because a real application key exists. Only the framework front controller may execute PHP, so a .php file anywhere else in the web root is refused rather than run:

grep -E '^\s+root ' /etc/nginx/sites-available/azuriom
stat -c '%n mode %a owner %U:%G' /var/www/azuriom/.env
for p in /.env /config/app.php /vendor/autoload.php /artisan /install /install/database /assets/anything.php; do
  printf '%-28s HTTP %s\n' "$p" "$(curl -s -o /dev/null -w '%{http_code}' -m 10 http://127.0.0.1$p)"
done
curl -s -o /dev/null -w 'the site itself: HTTP %{http_code}\n' http://127.0.0.1/

The nginx web root set to /var/www/azuriom/public, the environment file at mode 640 owned root www-data above the web root, every path above public returning HTTP 404 including .env config vendor and artisan, all four installer routes returning HTTP 404, a php file under assets returning HTTP 404, and the site itself returning HTTP 200

Step 14: Back up your site

Everything a customer creates lives in two places: the database, and the uploaded files under storage/app/public. Back up both, plus .env if you want an exact restore of the same instance.

sudo mysqldump --single-transaction --routines azuriom > /var/backups/azuriom-$(date +%F).sql
sudo tar czf /var/backups/azuriom-files-$(date +%F).tar.gz -C /var/www/azuriom storage/app/public

Restore on a new instance by copying the files back, importing the SQL dump into the azuriom database, and then clearing the cache. Keep .env out of any backup you share — it carries this VM's application key and database password.

Step 15: Keeping Azuriom up to date

Settings → Updates in the dashboard checks for new Azuriom releases and applies them in place. The OS itself is kept patched by unattended upgrades, which are enabled in the image:

systemctl is-enabled unattended-upgrades.service
grep -h . /etc/apt/apt.conf.d/20auto-upgrades

Troubleshooting

The site returns nothing and nginx is not running. Check whether first boot completed. If the marker is absent, the gate is doing its job and the real problem is in first boot:

systemctl status azuriom-firstboot.service --no-pager | tail -20
ls -l /var/lib/cloudimg/

A 500 error after editing .env. Laravel reads .env on every request, so a malformed line breaks every page. Check the application log and clear the cache:

sudo tail -20 /var/www/azuriom/storage/logs/laravel.log 2>/dev/null || echo "no application log yet"
sudo -u www-data php /var/www/azuriom/artisan optimize:clear

Password reset emails never arrive. Mail is unconfigured on a fresh instance by design. Configure SMTP as in Step 10.

Permission errors when installing a theme or plugin. The extension directories are owned by the web user on purpose; the rest of the application is deliberately read only to it. If a manual file copy has changed that, restore it:

sudo chown -R www-data:www-data /var/www/azuriom/storage /var/www/azuriom/bootstrap/cache /var/www/azuriom/plugins /var/www/azuriom/resources/themes

Support

cloudimg provides 24/7 technical support for this image by email at support@cloudimg.co.uk and via live chat, with a one hour average response time for critical issues. We help with domain and TLS certificate setup, mail delivery, themes and extensions, game server integration, roles and permissions, backup and restore, performance tuning and Azuriom version upgrades.

Azuriom is free software licensed under the GNU General Public License version 3 or later, with an additional term permitting the "Powered by Azuriom" attribution requirement on public pages. The upstream project's own documentation is at azuriom.com/docs.

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.