Applications Azure

AtroPIM on Ubuntu 24.04 on Azure User Guide

| Product: AtroPIM 1.16.5 on Ubuntu 24.04 LTS on Azure

Overview

AtroPIM is an open source Product Information Management system built on the AtroCore data platform. It gives a business one authoritative record for every product: names and identifiers, a configurable attribute model organised into attribute panels, hierarchical categories, classifications that apply whole attribute sets in a single step, brands, channels and localised values, and the images and documents that belong to each article. Everything the browser interface can do is also available over a REST API, so the catalogue can sit between your suppliers, your ERP and the sales channels that consume the data.

This image runs AtroPIM 1.16.5 on AtroCore 2.3.10 with PHP 8.4 under PHP FPM, nginx on port 80 and PostgreSQL 16 as the database, which is the engine upstream recommends. The AtroCore job manager runs from a systemd timer that executes the same console.php cron command upstream documents, so scheduled jobs, import feeds and export feeds are processed without any further setup.

The installation wizard is already completed inside the image. A stock AtroCore installation ends at a browser setup page that accepts database credentials and creates the first administrator with no authentication at all, which is not something that should ever face the public internet. This image drives that wizard during the build over the loopback interface, so every VM you launch starts at a login screen rather than at an open installer, and AtroCore removes the installer routes entirely once the system reports itself installed.

A unique administrator password is generated on the first boot of every VM, along with a unique database password, a unique password salt, a unique encryption key and a unique application identifier. Upstream fixes the salt and the encryption key at installation time, which would otherwise make them identical on every VM launched from one image; this image replaces all of them per VM, and holds the application back from serving anything until it has. Backed by 24/7 cloudimg support.

What is included:

  • AtroPIM 1.16.5 on AtroCore 2.3.10, pinned to exact stable releases and recorded in a composer.lock that ships with the image
  • The free Import, Import HTTP, Export and Export HTTP modules, so data can be moved in and out over files or HTTP with no additional licence
  • PHP 8.4 FPM, nginx on port 80 and PostgreSQL 16 bound to the loopback interface only
  • The AtroCore job manager driven by atropim-cron.timer, which runs upstream's own console.php cron every 60 seconds
  • The installation wizard completed at build time and its routes de registered, so no VM ever exposes an unauthenticated installer
  • A unique administrator password, database password, password salt, encryption key and application identifier generated on first boot and recorded in a root only file
  • PHP FPM and the job runner held back by a first boot marker, so nothing serves the application until every secret has been replaced
  • A small starter catalogue, one category, one attribute and three articles, so the interface opens onto working data
  • 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 comfortable starting point and matches upstream's own stated minimum: a measured idle appliance uses roughly 660 MiB of the 3.8 GiB available, with PHP FPM capped at five workers. Size up if you plan to import very large catalogues or run many concurrent editors. NSG inbound: allow 22/tcp from your management network and 80/tcp (and 443/tcp if you add TLS). AtroPIM is served over plain HTTP by default, so put your own domain and a trusted certificate in front of it before exposing it to the internet.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for AtroPIM 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 and Create.

Step 2 - Deploy from the Azure CLI

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

After the VM is created, open port 80 so you can reach the web interface:

az vm open-port --resource-group <your-rg> --name atropim --port 80 --priority 900

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

The first boot rotates every secret and writes a summary into the message of the day, so your first SSH session shows the AtroPIM URL and where the credentials file lives.

Step 4 - Confirm the services are running

AtroPIM runs as PHP FPM behind nginx, with PostgreSQL as its database and a systemd timer driving the AtroCore job manager. Confirm all four are active:

systemctl is-active postgresql@16-main.service php8.4-fpm.service nginx.service atropim-cron.timer

All four report active. The screenshot below also shows the exact release set read back out of the shipped composer.lock, and that only port 80 is reachable from outside the VM while PostgreSQL stays on the loopback interface.

All four AtroPIM services reporting active, the pinned AtroCore 2.3.10 and AtroPIM 1.16.5 release set read from the shipped composer.lock, and the listening sockets showing port 80 open while PostgreSQL is bound to loopback only

Step 5 - Confirm AtroPIM is answering

The image exposes a lightweight health endpoint that answers from nginx, and the application itself behind it:

curl -s -o /dev/null -w 'health   -> HTTP %{http_code}\n' http://127.0.0.1/healthz
curl -s -o /dev/null -w 'web UI   -> HTTP %{http_code}\n' http://127.0.0.1/
curl -s -o /dev/null -w 'REST API -> HTTP %{http_code}\n' http://127.0.0.1/api/

Expected output:

health   -> HTTP 200
web UI   -> HTTP 200
REST API -> HTTP 401

The 401 on the API is correct and is the useful signal here: it is PHP rejecting an unauthenticated caller. A 502 in that position would mean nginx is up but PHP FPM is not.

The next screenshot shows the mechanism that makes that possible. PHP FPM and the job runner both carry a ConditionPathExists on a marker that first boot creates only after every secret has been rotated, so on a brand new VM they are skipped until the rotation has finished. It also shows that the installer routes are gone, because the system already reports itself installed.

The php8.4-fpm and job runner units both gated on the cloudimg bootstrap marker, all units enabled, the marker present, isInstalled reporting true and all three AtroCore installer endpoints returning HTTP 404

Step 6 - Retrieve your administrator password

Every secret on this VM was generated during its first boot and written to a root only file:

sudo stat -c 'mode %a owner %U:%G' /root/atropim-credentials.txt
sudo grep -E '^ATROPIM_URL|^ATROPIM_ADMIN_USER' /root/atropim-credentials.txt

Expected output:

mode 600 owner root:root
ATROPIM_URL=http://<your-vm-public-ip>/
ATROPIM_ADMIN_USER=admin

To read the password itself:

sudo grep '^ATROPIM_ADMIN_PASSWORD=' /root/atropim-credentials.txt | cut -d= -f2-

The same file also records ATROPIM_DB_USER and ATROPIM_DB_PASSWORD for the PostgreSQL role AtroPIM uses.

Step 7 - Confirm the default logins are rejected

AtroCore evaluates a username and password at exactly one route, /api/userSession, which returns a session token used for every other call. That makes it the right place to prove no guessable credential works on your VM:

for D in admin password atrocore atropim 123456; do
  printf 'admin / %-9s -> HTTP %s\n' "$D" \
    "$(curl -s -o /dev/null -w '%{http_code}' -m 10 \
       -H "Authorization-Token: $(printf 'admin:%s' "$D" | base64 -w0)" \
       http://127.0.0.1/api/userSession)"
done

Expected output:

admin / admin     -> HTTP 401
admin / password  -> HTTP 401
admin / atrocore  -> HTTP 401
admin / atropim   -> HTTP 401
admin / 123456    -> HTTP 401

The AtroPIM credentials file at mode 600 owned by root, the three seeded accounts with only admin holding a password, all five guessable administrator passwords rejected with HTTP 401, and PostgreSQL bound to localhost with the superuser refusing a password less TCP login

The two accounts shown alongside admin are AtroCore's internal system accounts. Neither holds a password, so neither can sign in.

Step 8 - Query the catalogue over the REST API

Sign in once to mint a session token, then use that token for every other call:

PW=$(sudo grep '^ATROPIM_ADMIN_PASSWORD=' /root/atropim-credentials.txt | cut -d= -f2-)
TOKEN=$(curl -fsS -m 30 \
  -H "Authorization-Token: $(printf 'admin:%s' "$PW" | base64 -w0)" \
  http://127.0.0.1/api/userSession \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["user"]["token"])')
AUTH="Authorization-Token: $(printf 'admin:%s' "$TOKEN" | base64 -w0)"
curl -s -H "$AUTH" 'http://127.0.0.1/api/Product?maxSize=10' \
  | python3 -m json.tool | head -30

That returns the starter catalogue, and the same records are visible directly in PostgreSQL, which is the proof the application really is reading and writing the database on this VM rather than a cache:

sudo runuser -u postgres -- psql -P pager=off \
  -c "SELECT number, name, status FROM product WHERE deleted = false ORDER BY number" atropim

Expected output:

  number  |       name       | status
----------+------------------+--------
 SKU-0001 | Sample Product A | ready
 SKU-0002 | Sample Product B | ready
 SKU-0003 | Sample Product C | ready
(3 rows)

The three starter articles queried straight out of PostgreSQL with their SKU numbers and ready status, the sample category, the REST API and psql reporting the same product count, and the five AtroCore job manager daemons running

Step 9 - Open AtroPIM in your browser

Browse to http://<your-vm-public-ip>/. AtroPIM presents its sign in page.

The AtroPIM sign in page with username and password fields, a remember username option and the Login button

Step 10 - Sign in

Sign in as admin with the password from Step 6. The dashboard opens with a Getting Started panel and the full list of entities the platform exposes.

The AtroPIM dashboard after signing in, showing the First Steps panel with setup guidance and the Entities panel listing Accounts, Attributes, Attribute Panels, Classifications and the rest of the data model

Step 11 - Open the Products catalogue

Choose Products in the top navigation. The grid shows the three starter articles with their article numbers, names and status, and the category tree sits alongside it.

The AtroPIM Products grid listing SKU 0001, SKU 0002 and SKU 0003 as Sample Product A, B and C, each with a green Ready status, next to the category tree showing the Sample Catalogue node

Step 12 - Open a product record

Click a product name to open its record. The detail view is organised into the panels that make up your data model: Master Data, State, Description, Taxonomy and the attribute panels you define.

The Sample Product A record open in AtroPIM, showing the Master Data panel with the article name and SKU 0001 number, the recommended retail price and sort order, and the State panel showing a Ready status

Step 13 - Edit product information

Use Edit, or the inline pencil beside any field, to change a value and save it. The change is written straight to PostgreSQL and rendered back immediately.

The Sample Product A record after editing, with the Description panel showing the newly saved text rendered back in the browser

Step 14 - Work with categories

Categories are hierarchical and are managed from the tree in the left panel or from the Categories view. Products can belong to several categories, and category structure is what most downstream channels consume.

The AtroPIM Categories view listing the Sample Catalogue category with its code, alongside the category tree panel

Step 15 - Shape the attribute model

Attributes are the heart of a PIM. Each attribute has a type, belongs to an attribute panel and is bound to an entity, and you create them from the browser rather than from code.

The AtroPIM Attributes view showing the Material attribute with system name material, bound to the Product entity and placed on the Attributes panel

From here the usual next steps are to remove the starter catalogue, define the attributes your own articles need, group them into panels, and then load real data through the Import module or the REST API.

Server components

Component Version Notes
AtroPIM 1.16.5 The Product Information Management application
AtroCore 2.3.10 The underlying data platform
Import / Import HTTP 1.11.4 / 1.7.3 Free import modules, files and HTTP sources
Export / Export HTTP 1.11.5 / 1.9.3 Free export modules, files and HTTP targets
PHP 8.4 FPM and CLI, from the ondrej PHP packages
PostgreSQL 16 Bound to the loopback interface only
nginx 1.24 Serves port 80 and proxies to PHP FPM
Ubuntu 24.04 LTS Fully patched, unattended security upgrades enabled

Filesystem layout

Path Purpose
/var/www/atropim Application root, holds composer.json, composer.lock and vendor/
/var/www/atropim/public The nginx document root, the only part of the tree served over HTTP
/var/www/atropim/data Configuration, cache and generated metadata
/var/www/atropim/upload Uploaded product images, datasheets and other assets
/var/lib/postgresql/16/main The PostgreSQL cluster
/root/atropim-credentials.txt Per VM credentials, mode 600, owned by root
/var/lib/cloudimg First boot sentinel and the bootstrap ready marker

Managing the services

systemctl status php8.4-fpm.service --no-pager | head -5

Restart the application after a configuration change:

sudo systemctl restart php8.4-fpm.service nginx.service

The job manager is driven by a timer that runs upstream's own cron entry every 60 seconds:

systemctl list-timers atropim-cron.timer --no-pager | head -3

Clear the AtroCore cache after changing the data model from the command line:

cd /var/www/atropim && sudo runuser -u www-data -- php console.php clear cache

Maintenance

Add your own domain name and a trusted TLS certificate before putting this VM in front of real users. The nginx site lives at /etc/nginx/sites-available/atropim and serves plain HTTP on port 80 only; adding a certificate is the single most important change to make.

Back up both halves of the system together: the PostgreSQL database holds your catalogue, and /var/www/atropim/upload holds the files attached to it.

sudo -u postgres pg_dump atropim | gzip > /var/backups/atropim-$(date +%F).sql.gz
sudo tar czf /var/backups/atropim-upload-$(date +%F).tar.gz -C /var/www/atropim upload

AtroCore updates itself through its own Module Manager, which uses the composer.phar at the application root. That file sits above the document root and is not reachable over HTTP.

Security recommendations

  • Restrict 22/tcp to your management network and put a certificate in front of port 80
  • Change the administrator password after your first sign in, and create named user accounts rather than sharing the administrator
  • Keep PostgreSQL on the loopback interface; nothing outside the VM needs to reach it
  • Take the starter catalogue out once you have loaded your own data
  • Leave unattended security upgrades enabled so the base operating system keeps patching itself

Support

cloudimg provides 24/7 support for this image. For questions about AtroPIM itself, the upstream Help Center and community are the best starting points.