Atomic CRM on Ubuntu 24.04 on Azure User Guide
Overview
This image runs Atomic CRM, a full featured open source customer relationship management application, on Ubuntu 24.04 LTS. It ships the Atomic CRM web application together with a self-hosted Supabase backend, so your whole CRM - every contact, company, deal and note - lives inside your own Azure subscription rather than a hosted service.
Atomic CRM self-hosts as a small, memory-lean set of services, orchestrated by Docker Compose and managed by systemd:
- nginx - serves the Atomic CRM web application at
/and fronts the API (port 80) - kong - the Supabase API gateway (validates the API key, routes
/rest,/auth,/storage) - gotrue - the authentication service (email/password sign-in, at
/auth/v1) - postgrest - the data API over PostgreSQL (at
/rest/v1) - storage + imgproxy - file storage and image resizing for attachments, logos and avatars
- postgres - the CRM database (contacts, companies, deals, notes, tasks) and the auth catalogue
Everything is reached through nginx on the single public address, so there is only one port (80) to open. An unauthenticated /healthz endpoint answers load-balancer probes.
What is included:
- The Atomic CRM single page web application, built and served as static assets by nginx
- A slimmed self-hosted Supabase backend (Postgres, PostgREST, GoTrue, Storage, imgproxy, Kong) under Docker Compose via an
atomic-crm.servicesystemd unit, from images pinned by version - The web app and the whole API on port 80 behind nginx, same-origin (no CORS, no extra ports)
- No baked credential. Every secret is generated fresh per-VM on first boot - the PostgreSQL password, the JWT signing secret (from which the public API keys are re-derived), and a first administrator account with its own generated password - so no upstream Supabase default (the well known demo keys and passwords) survives into the image
- The CRM database and uploaded files on a dedicated Azure data disk at
/var/lib/atomic-crm, captured into the image - An unauthenticated
/healthzendpoint for Azure Load Balancer health probes - Unattended security upgrades enabled on the base OS
Prerequisites
- An Azure subscription and permission to create a VM.
- An SSH key pair to administer the VM (
azureuser). - Inbound HTTP (80) open to the clients that need the CRM, and SSH (22) from your admin IP.
- The recommended size is Standard_D2s_v3 (2 vCPU / 8 GB) for comfortable steady-state headroom.
Step 1 - Launch the VM
Option A - Azure Portal
- From the cloudimg Atomic CRM on Ubuntu 24.04 LTS offer, choose Create.
- Pick your resource group and region, and set the VM size to Standard_D2s_v3.
- Set the admin username to
azureuserand upload your SSH public key. - Under Networking, allow inbound SSH (22) from your admin IP and HTTP (80) from the clients that need the web app.
- Review and create.
Option B - Azure CLI
az vm create \
--resource-group <your-resource-group> \
--name atomic-crm \
--image <cloudimg-atomic-crm-image-urn> \
--size Standard_D2s_v3 \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
Open port 80 to the clients that need the web app (SSH is opened for your admin IP separately):
az network nsg rule create \
--resource-group <your-resource-group> \
--nsg-name <your-nsg> \
--name allow-http --priority 1001 \
--destination-port-ranges 80 --access Allow --protocol Tcp
For a stable experience, assign a static public IP - the sign-in URL and API base are pinned to the VM's public IP on first boot.
Step 2 - First boot and the per-VM credentials
On the first boot, atomic-crm-firstboot.service generates a fresh set of secrets unique to this VM, pins the public address, applies the database schema on a clean database, creates your administrator account, and brings the stack up. Give it a minute or two on the first boot, then SSH in:
ssh azureuser@<vm-ip>
Read your per-VM sign-in details (the secret values are unique to your VM; they are masked below):
sudo cat /root/atomic-crm-credentials.txt

The file records the CRM URL, your administrator sign-in (admin@atomic-crm.local plus a per-VM password), and the database password. Store them somewhere safe.
Step 3 - Verify the stack
Confirm all seven services are running:
sudo docker compose -f /opt/atomic-crm/docker-compose.yml ps

Step 4 - Health check
Check the web front, the auth service and the data API through nginx. The API correctly returns 401 when called with no key and when called with the upstream demo default key, which confirms that authentication is enforced and that no well known credential governs your server:
ANON="$(sudo grep '^ANON_KEY=' /opt/atomic-crm/.env | cut -d= -f2-)"
curl -s -o /dev/null -w "healthz: %{http_code}\n" http://127.0.0.1/healthz
curl -s -o /dev/null -w "auth (with key): %{http_code}\n" http://127.0.0.1/auth/v1/health -H "apikey: $ANON"
curl -s -o /dev/null -w "rest (no key): %{http_code}\n" http://127.0.0.1/rest/v1/companies?select=id

Step 5 - Sign in
Open http://<vm-ip>/ in your browser. Atomic CRM presents a sign-in page; enter the administrator email and password from your credentials file.

Step 6 - Your dashboard
After signing in you land on the dashboard, with the top navigation for Dashboard, Contacts, Companies and Deals, and a "What's next?" panel to guide your first steps.

Step 7 - Companies, contacts and deals
Open Companies to see your organisations as cards, each showing its contacts and open deals. Use the search box and the size and sector filters on the left to narrow the list, and New Company to add one.

Open Deals for the pipeline as a kanban board: deals are grouped into columns by stage - Opportunity, Proposal Sent, In Negotiation, Won, Lost, Delayed - with a running total per column. Drag a card between columns to move a deal along, and New Deal to add one. Contacts works the same way, with each contact linked to its company and deals, and notes and tasks kept against the record.

Adding more users
This appliance ships ready to use for the administrator account, and no email server is configured, so the in-app invite workflow (which needs SMTP) is off by default. To add another user, create the account directly against the authentication service with the per-VM service-role key, which is in /opt/atomic-crm/.env:
SVC="$(sudo grep '^SERVICE_ROLE_KEY=' /opt/atomic-crm/.env | cut -d= -f2-)"
curl -s -X POST http://127.0.0.1/auth/v1/admin/users \
-H "apikey: $SVC" -H "Authorization: Bearer $SVC" -H 'Content-Type: application/json' \
-d '{"email":"teammate@example.com","password":"choose-a-strong-password","email_confirm":true,
"user_metadata":{"first_name":"Team","last_name":"Mate"}}'
The new user can sign in immediately at http://<vm-ip>/. The first account created on the server is the administrator; everyone after is a regular user. To enable the in-app invite and password-reset flows, configure an SMTP provider in /opt/atomic-crm/.env (the SMTP_* values) and restart the stack - see the Atomic CRM documentation for details.
Data and backups
All CRM data lives in PostgreSQL and the uploaded files in the storage volume, both on the dedicated data disk at /var/lib/atomic-crm (relocated Docker data-root). Snapshot that disk with the Azure Backup service, or take a logical dump:
sudo docker compose -f /opt/atomic-crm/docker-compose.yml exec -T db \
pg_dump -U postgres postgres | gzip > atomic-crm-backup-$(date +%F).sql.gz
Troubleshooting
- The page shows a blank "Loading..." screen - confirm you are browsing the VM's own public IP (the API base is pinned to it at first boot) and that port 80 is open in your NSG.
atomic-crm-firstboot.servicedid not finish - checkjournalctl -u atomic-crm-firstboot.service; it applies the schema and creates the admin on first boot and can take a minute or two.- Sign-in fails - re-read
/root/atomic-crm-credentials.txt; the administrator password is generated per-VM. - Restart the stack -
sudo systemctl restart atomic-crm.service.
Support
This image is built and maintained by cloudimg. For deployment help or issues, contact cloudimg support - support is included 24/7 with every cloudimg image.