E-commerce Azure

FoodCoopShop on Ubuntu 24.04 on Azure User Guide

| Product: FoodCoopShop on Ubuntu 24.04 LTS on Azure

Overview

FoodCoopShop is the user-friendly open source software for food cooperatives, buying groups and small local producers. Members browse a shared catalogue of products from many producers, place orders for a weekly delivery or pickup rhythm, and manage their account and credit balance; administrators manage producers, products, categories, orders, invoicing and pickup days from a dedicated admin area. It supports many delivery rhythms, a self-service stock mode with optional barcode scanning, order adaptions such as cancellation and weight or price changes, and a cashless payment system based on account credit.

The cloudimg image delivers FoodCoopShop fully installed and configured on Ubuntu 24.04 — a CakePHP 5 application on PHP 8.4 with OPcache, served by nginx with php-fpm, backed by MySQL 8.0. Database migrations and the base data are already applied and the shop is set to English, so you land directly on a working shop and admin area; there is no setup wizard to complete. Backed by 24/7 cloudimg support.

What is included:

  • FoodCoopShop 4.2.0 (AGPL-3.0), served from /var/www/foodcoopshop/webroot
  • nginx + PHP 8.4 (php8.4-fpm with OPcache) + MySQL 8.0
  • A per-VM CakePHP Security.salt and cookieKey, a per-VM MySQL password and a per-VM administrator email and password, all generated at first boot and written to a root-only file
  • MySQL bound to 127.0.0.1 only; nginx on 80/tcp is the only public surface
  • An example catalogue (a demo producer with three products) so the admin and storefront show a working shop from the first boot
  • nginx.service, php8.4-fpm.service and mysql.service as systemd units, enabled and active; foodcoopshop-firstboot.service rotates the secrets before nginx serves the first request
  • 24/7 cloudimg support

FoodCoopShop sign-in page

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting point; scale up for larger cooperatives or heavier catalogues. NSG inbound: allow 22/tcp from your management network and 80/tcp (plus 443/tcp once you enable HTTPS) from your members.

Step 1 — Deploy from the Azure Marketplace

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

Step 2 — Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name foodcoopshop \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

az vm open-port --resource-group <your-rg> --name foodcoopshop --port 80 --priority 1010

Step 3 — Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 — Confirm the services are running

The three services that back FoodCoopShop should all report active, the shop front answers HTTP 200, and MySQL listens on loopback only:

systemctl is-active nginx php8.4-fpm mysql
curl -s -o /dev/null -w 'shop front / -> HTTP %{http_code}\n' http://127.0.0.1/
ss -ltn | grep -E ':80 |:3306'

Expected: three lines of active, then HTTP 200, and a :3306 socket bound to 127.0.0.1 only (never 0.0.0.0).

Service status, PHP version and listening sockets

Step 5 — Retrieve your administrator credentials

On the first boot of every VM, a one-shot service (foodcoopshop-firstboot.service) generates values that are unique to that VM: a fresh CakePHP application Security.salt and cookieKey, a fresh MySQL password, and a fresh administrator email and password. The VM's own address is set as the application base URL automatically. No shared or default credentials ship in the image, and nginx does not serve a single request until this rotation is complete.

sudo cat /root/foodcoopshop-credentials.txt

The file (mode 0600, root only) contains the administrator email (foodcoopshop.admin.user), the administrator password (foodcoopshop.admin.pass), the shop URL and the database credentials. FoodCoopShop signs in by email.

Per-VM credentials file

Step 6 — Sign in to the admin area

Browse to http://<vm-public-ip>/sign-in and sign in with the administrator email and password from the credentials file. You land on the admin panel, where you manage producers, products, orders, members and the website.

FoodCoopShop admin panel after sign-in

You can also prove the login round-trip from the VM's own shell. FoodCoopShop protects the login form with both a CSRF token and CakePHP FormProtection, so a real sign-in replays all of them; a successful login answers HTTP 302 and the authenticated GET /admin then answers HTTP 200:

U=$(sudo grep '^foodcoopshop.admin.user=' /root/foodcoopshop-credentials.txt | cut -d= -f2-)
P=$(sudo grep '^foodcoopshop.admin.pass=' /root/foodcoopshop-credentials.txt | cut -d= -f2-)
CJ=$(mktemp); PG=$(mktemp)
curl -s -c "$CJ" http://127.0.0.1/sign-in -o "$PG"
CSRF=$(grep -oE 'name="_csrfToken" value="[^"]*"' "$PG" | head -1 | sed -E 's/.*value="([^"]*)".*/\1/')
TF=$(grep -oE 'name="_Token\[fields\]" value="[^"]*"' "$PG" | head -1 | sed -E 's/.*value="([^"]*)".*/\1/')
TU=$(grep -oE 'name="_Token\[unlocked\]" value="[^"]*"' "$PG" | head -1 | sed -E 's/.*value="([^"]*)".*/\1/')
curl -s -o /dev/null -b "$CJ" -c "$CJ" -w 'sign-in POST     -> HTTP %{http_code}\n' \
  --data-urlencode "_csrfToken=$CSRF" --data-urlencode "_Token[fields]=$TF" \
  --data-urlencode "_Token[unlocked]=$TU" --data-urlencode "email=$U" \
  --data-urlencode "passwd=$P" --data-urlencode "remember_me=0" http://127.0.0.1/sign-in
curl -s -o /dev/null -b "$CJ" -w 'GET /admin (authed) -> HTTP %{http_code}\n' http://127.0.0.1/admin
rm -f "$CJ" "$PG"

First-boot rotation and authenticated round-trip

Step 7 — Manage producers and the catalogue

From the admin panel's left navigation, open Manufacturers to manage your producers. The image ships a demo producer, cloudimg Demo Producer, with three example products, so you can see the catalogue working immediately. Add your own producers, then use Products to add and price their products, Members to manage member accounts, and Orders to view and adapt orders.

Admin producer management showing the seeded demo producer

The member-facing storefront at http://<vm-public-ip>/ presents the shop with search, the producer list, the shopping cart and each member's credit balance. Configure your delivery and pickup rhythm under Website administration so members can place orders for the next delivery day.

FoodCoopShop storefront

Step 8 — Confirm the version and the seeded catalogue

cat /var/www/foodcoopshop/VERSION.txt
sudo mysql foodcoopshop -e "SELECT
  (SELECT count(*) FROM fcs_manufacturer WHERE active=1) AS producers,
  (SELECT count(*) FROM fcs_product WHERE active=1) AS active_products;"

The version file reports 4.2.0, and the seeded example catalogue shows one active producer with three active products.

Version and seeded catalogue

Administration from the CLI

FoodCoopShop is a CakePHP application; admin CLI tasks run through bin/cake as the www-data user, for example to list the available commands:

sudo -u www-data php8.4 /var/www/foodcoopshop/bin/cake.php

The application configuration lives in /var/www/foodcoopshop/config/custom_config.php (database, Security.salt, cookieKey and App.fullBaseUrl). Scheduled tasks such as order reminders and invoicing are run by FoodCoopShop's queue; see the FoodCoopShop cronjobs documentation.

Enabling HTTPS

For production, point a real domain at the VM's public IP, open 443/tcp in the NSG, then terminate TLS at nginx with Let's Encrypt (replace the domain):

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com

After the certificate is issued, set your new address as the shop base URL in config/custom_config.php (the App.fullBaseUrl value, with no trailing slash), for example https://your-domain.example.com, then reload nginx and php-fpm.

Backup and maintenance

Back up the MySQL database (the database password is in the credentials file):

sudo mysqldump foodcoopshop > foodcoopshop-backup.sql

Snapshot the VM's OS disk in Azure for a point-in-time backup of the application and database together. Keep the OS patched with sudo apt update && sudo apt upgrade (unattended security upgrades are enabled by default). The stack restarts cleanly with sudo systemctl restart nginx php8.4-fpm mysql.

Support

This image is backed by 24/7 cloudimg support. Contact us by email and chat for help with FoodCoopShop deployment, upgrades, delivery-rhythm and pickup-day configuration, email and SMTP setup, invoicing and payments, and database administration.

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.