Foodsoft on Ubuntu 24.04 on Azure User Guide
Overview
Foodsoft is an open source web application for running a nonprofit food cooperative or buying group. Members browse supplier catalogues and place orders together as a group, and the coop settles who ordered what and what each member owes. It keeps supplier and article lists, opens and closes ordering rounds, tallies each order group's totals, tracks member and working-group balances and financial transactions, and organises the shared tasks that keep a coop running. The cloudimg image installs Foodsoft 4.9.2 as the official upstream Docker stack and runs it behind nginx as a systemd service, so a complete food-coop management system is online within minutes of launch.
What is included:
- Foodsoft 4.9.2 (AGPL-3.0) deployed from the official upstream Docker image
foodcoops/foodsoft:4.9.2, pinned so the image never silently upgrades at build time - Docker Engine (Docker CE) + the Docker Compose plugin, installed from the official Docker package repository
- The Foodsoft Compose stack: the
appRails web application (Puma on container port 3000), theworkerResque background worker (emails and order finalisation), acronscheduled-task runner,db(MariaDB 11.4) andredis(Redis 7.2, the Resque queue) - A system
nginxreverse proxy on port 80 forwarding to the Rails app on127.0.0.1:3000, plus an unauthenticated static/cloudimg-healthendpoint - A per-VM administrator password created at first boot, with a fresh Rails
SECRET_KEY_BASEand a fresh database password rotated per instance - the upstream default admin login is rotated away before the web app is ever reachable - The database and Redis bind to the internal Docker network only; nothing but nginx on port 80 is published on the public interface
- Three systemd units:
nginx.service,foodsoft.service(a oneshot wrapper arounddocker compose up -d) andfoodsoft-firstboot.service - 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key, and a VNet plus subnet. Standard_B2s (2 vCPU / 4 GB RAM) is the recommended size. NSG inbound rules: allow 22/tcp from your management CIDR for SSH and 80/tcp from the CIDR that needs the Foodsoft web app (add 443/tcp if you enable HTTPS).
Step 1: Connect over SSH
Replace <vm-ip> with the public IP of your VM. The default login user is azureuser.
ssh azureuser@<vm-ip>
Step 2: Confirm the services are active
sudo systemctl is-active docker.service nginx.service foodsoft.service foodsoft-firstboot.service
cd /opt/foodsoft && sudo docker compose ps --format "table {{.Name}}\t{{.Status}}"
You should see active printed for each systemd unit, and the five Compose containers - foodsoft-app, foodsoft-worker, foodsoft-cron, foodsoft-db and foodsoft-redis - all in the Up state.

Step 3: Confirm only port 80 is public
The nginx reverse proxy serves an unauthenticated static /cloudimg-health endpoint on port 80, and the Foodsoft login page answers on /f/login. The Rails app listens on loopback only, and the database and Redis are not published to any host port at all.
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1/f/login
curl -s http://127.0.0.1/cloudimg-health
sudo ss -tlnH | awk '{print $4}' | sort -u
/f/login returns 200, /cloudimg-health returns OK, and the listening sockets show only :80 (nginx) and :22 (SSH) on the public interface, with the Rails app on 127.0.0.1:3000. MariaDB and Redis do not appear - they are reachable only on the internal Docker network.

Step 4: Read the per-VM admin credentials
A unique administrator password is generated for your instance on its first boot and written to a root-only file, along with a fresh Rails secret key base and database password.
sudo cat /root/foodsoft-credentials.txt
This file lists FOODSOFT_URL (where to sign in), FOODSOFT_LOGIN_URL, FOODSOFT_ADMIN_USER (admin) and FOODSOFT_ADMIN_PASSWORD. Store the password somewhere safe.

Step 5: Confirm the default login has been rotated away
Foodsoft's upstream seed creates an initial admin user with the well-known password secret. On this image, first boot rotates that password to your per-VM value before the web app is ever exposed, so the default login is dead.
cd /opt/foodsoft
sudo docker exec foodsoft-app bundle exec rails runner \
'puts User.authenticate("admin","secret") ? "DEFAULT STILL LIVE" : "default admin/secret REJECTED"'
The output is default admin/secret REJECTED, confirming the seeded default credential no longer works.

Step 6: Sign in to Foodsoft
Open http://<vm-ip>/ in your browser. Foodsoft redirects you to the foodcoop login page at /f/login. Enter the user admin and the FOODSOFT_ADMIN_PASSWORD from Step 4, then click Login.
You can also verify the credentials from the command line - it prints LOGIN_OK when the per-VM password authenticates:
PASS=$(sudo grep '^FOODSOFT_ADMIN_PASSWORD=' /root/foodsoft-credentials.txt | cut -d= -f2-)
cd /opt/foodsoft
sudo docker exec -e RT_PASS="$PASS" foodsoft-app bundle exec rails runner \
'puts User.authenticate("admin", ENV["RT_PASS"]) ? "LOGIN_OK" : "LOGIN_FAIL"'

Step 7: Explore the dashboard
After signing in you land on the Foodsoft dashboard - it shows the current ordering rounds, your order group's balance and engagement, and quick links to members, orders, products and finances.

Step 8: Manage your suppliers
Open Articles -> Suppliers/articles to manage the suppliers your coop buys from. Each supplier holds its own article catalogue, stock and deliveries. Use Create new supplier to add your own.

Step 9: Build your article catalogue
Open a supplier to manage its article catalogue - names, categories, units, net prices, VAT and deposit. You can add articles one at a time, upload a spreadsheet, or open a new ordering round for the coop directly from here.

From here the day-to-day flow is: open an order for a supplier, let members place their orders during the ordering window, close the order, and balance it in the finance area so each order group's account is charged its share.
Managing the stack
The Foodsoft stack is wrapped by the foodsoft.service systemd unit, which runs docker compose up -d from /opt/foodsoft.
sudo systemctl status foodsoft.service --no-pager
To inspect the application logs, tail the app or worker container:
cd /opt/foodsoft
sudo docker compose logs --tail 50 app
To restart the whole stack after a configuration change, run sudo systemctl restart foodsoft.service.
Configuring email
Out of the box no SMTP server is configured, so Foodsoft does not send email (invites, password resets and order notifications are silently held back rather than failing). To enable email, add your SMTP settings to /opt/foodsoft/.env and restart the stack. Set SMTP_ADDRESS, and as needed SMTP_PORT, SMTP_DOMAIN, SMTP_USER_NAME, SMTP_PASSWORD and SMTP_ENABLE_STARTTLS_AUTO=true, then run sudo systemctl restart foodsoft.service.
Enabling HTTPS
The image serves plain HTTP on port 80. For any public deployment, put TLS in front with your own domain. Point a DNS A record at the VM public IP, then install a certificate with certbot's nginx plugin. Outline:
- Install certbot and the nginx plugin from the Ubuntu repositories.
- Run certbot against your-domain.example to obtain and install a certificate into the nginx site.
- Reload nginx.
Use your own registered domain in place of any example placeholder.
Maintenance
The OS receives unattended security updates. To update Foodsoft itself, edit the pinned image tag in /opt/foodsoft/docker-compose.yml, then from /opt/foodsoft run sudo docker compose pull, apply any database migrations with sudo docker compose run --rm app bundle exec rake db:migrate, and run sudo systemctl restart foodsoft.service. Always back up the foodsoft_db_data Docker volume before a major version upgrade.
Support
Backed by 24/7 cloudimg support. Foodsoft is developed by the foodcoops community and licensed under the AGPL-3.0. This image is provided by cloudimg and is not affiliated with or endorsed by the Foodsoft project.