DomainMOD 4.23.0 on AWS User Guide
Overview
This guide covers launching and using the DomainMOD 4.23.0 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg help desk, IT service and project management listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
DomainMOD is an open source application for managing domains and other internet assets in one place. It records every domain you own with its registrar, expiry date, renewal fee, owner, category and DNS provider, tracks SSL certificates, hosting accounts and IP addresses the same way, sends expiration e-mails, reports renewal costs across currencies, and can pull your domain list straight from supported registrar APIs through its domain queue.
What the cloudimg image adds:
- No installation wizard and no default login. DomainMOD's own installer creates the administrator with the password
adminand offers no way to choose another. This image runs the installer at build time, replaces that password at once (and provesadminis refused), then deletes theinstall/directory; nginx returns 404 for/install/as well. - Passwords unique to your instance. The DomainMOD
adminpassword and the MariaDB password DomainMOD uses are generated on first boot and written to/root/domainmod-credentials.txt(root only). DomainMOD then asks you to choose your own password after you first sign in. The image ships with the database account locked, and nginx and PHP-FPM refuse to start until first boot has finished. - HTTPS from the first boot. nginx serves DomainMOD on port 443 with a certificate generated on your instance, port 80 redirects to HTTPS, requests for a host name the instance does not serve are dropped, and DomainMOD's own HTTPS switch is on, so the session cookie is
Secure. - Only the application on the web. Upstream ships no web server rules at all. On this image the configuration file (it holds the database password), the class library, the vendored dependencies, the scheduler and upgrade entry points, and DomainMOD's
temp/directory (its backup page writes a full database dump there) answer 404. - The scheduler on a timer, not a public URL. DomainMOD's task scheduler runs every ten minutes from a systemd timer instead of a
cron.phpweb request anyone could trigger. - Hardened defaults. MariaDB listens on
127.0.0.1only, with its data on a dedicated 20 GiB EBS volume. nginx, PHP-FPM, MariaDB, the scheduler and the first boot service are blocked from the instance metadata service (DomainMOD calls registrar APIs and other remote services), the AMI requires IMDSv2, and PHP is handed the configured host name and the real client address from the connection, never values a client sends. Cross site form posts are refused. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: DomainMOD 4.23.0 (the upstream v4.23.0 release, checksum verified), nginx, PHP 8.3 FPM with OPcache, MariaDB, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
- A security group that allows TCP 22 (SSH) from your admin network, and TCP 443 (DomainMOD) and TCP 80 (redirects to HTTPS) from the networks your users work from. Never open 3306. DomainMOD can hold registrar API credentials, so keep 80 and 443 as narrow as you can.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick DomainMOD 4.23.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair and a security group as above. The AMI brings two volumes: the root volume and a 20 GiB data volume for the database.
With the AWS CLI (replace the values in angle brackets):
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=domainmod}]'
First boot takes well under a minute after the instance is running.
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| DomainMOD 4.23.0 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot sets the passwords and the certificate, then nginx and PHP-FPM start:
systemctl is-active nginx php8.3-fpm mariadb domainmod-firstboot
sudo test -f /var/lib/cloudimg/domainmod-firstboot.done && echo FIRSTBOOT_DONE
All four units are active and the first boot marker is present:
active
active
active
active
FIRSTBOOT_DONE
Only SSH and nginx listen on network ports; MariaDB listens on the loopback address and PHP-FPM uses a local socket:
sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | sort
0.0.0.0:22 users:(("sshd",...))
0.0.0.0:443 users:(("nginx",...))
0.0.0.0:80 users:(("nginx",...))
127.0.0.1:3306 users:(("mariadbd",...))
[::]:22 users:(("sshd",...))
[::]:443 users:(("nginx",...))
[::]:80 users:(("nginx",...))
Step 4: Get your URL and password
sudo cat /root/domainmod-credentials.txt
The file is readable by root only:
# DomainMOD 4.23.0 - generated on first boot by domainmod-firstboot.service
# These credentials are unique to this instance; none was baked into the image. Keep this file secret.
DOMAINMOD_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/
DOMAINMOD_ADMIN_USER=admin
DOMAINMOD_ADMIN_PASSWORD=<DOMAINMOD_ADMIN_PASSWORD>
MARIADB_DATABASE=domainmod
MARIADB_USER=domainmod
MARIADB_PASSWORD=<MARIADB_PASSWORD>
# DomainMOD asks you to choose your own password after you first sign in.
# The certificate is self signed (minted on this instance); port 80 redirects to https.
# Change the name DomainMOD is served as: sudo domainmod-set-hostname <your-dns-name>
# MariaDB listens on 127.0.0.1 only; on-box maintenance: sudo mariadb
# User guide: https://www.cloudimg.co.uk/guides/domainmod-4-23-0-on-ubuntu-24-04-aws/
The URL uses the instance's public DNS name from the EC2 instance metadata. The certificate also names the public and private IP addresses, so https://<public-ip>/ works too. If you stop and start the instance without an Elastic IP, the boot service picks up the new address and issues a new certificate for it. To serve DomainMOD under your own DNS name, see Step 9.
You only need the MariaDB password for your own tools; DomainMOD already has it in /var/www/domainmod/_includes/config.inc.php.
Step 5: Sign in and choose your password
Open the DOMAINMOD_URL in your browser. The certificate is self signed, so your browser asks you to confirm it the first time (or install your own, Step 9). Sign in as admin with the password from Step 4:

DomainMOD takes you straight to Change Password, because the first boot password is only meant to get you in. Enter a password of your own twice and select Change Password:

Then delete or update DOMAINMOD_ADMIN_PASSWORD in /root/domainmod-credentials.txt so it does not hold an old password. The Dashboard shows the system totals, with the full navigation on the left and the DomainMOD version in the footer:

You can also check the sign in from the instance. This script signs in through the real login form over HTTPS with the instance's first boot password, then shows that upstream's default admin, a blank password and common weak passwords are refused. Run it before you change the password (afterwards it reports that the first boot password no longer works, which is what you want):
sudo domainmod-cred-roundtrip.sh
OK per-instance admin signs in over HTTPS; upstream's admin/admin, blank and weak guesses refused
Step 6: Add your registrars and domains
DomainMOD organises domains under the registrar they are registered with, so a registrar and a registrar account come first. Open Domains and DomainMOD says exactly that:

Select Click here to add a Domain Registrar, save it, then add a registrar account under it. After that you can add domains one at a time, import them in bulk with the CSV importer on the same page, or, for the registrars DomainMOD supports, save the account's API credentials and let the Domain Queue pull your domain list and expiry dates from the registrar.
SSL Certificates does the same for your certificates, with their own providers, accounts and types. Assets holds the records those point at (owners, categories, DNS profiles, IP addresses, web hosts), Segments groups domains you want to report on together, Reporting turns the fee data into renewal cost reports in your currency, and Bulk Updater changes many records at once.
Step 7: E-mail and the installation URL
DomainMOD sends expiration e-mails and password reset links. Set your SMTP server under Administration, System Settings. Amazon SES works as the relay (port 587 with STARTTLS); allow outbound 587 if you restrict egress. Each user subscribes to expiration e-mails in their User Profile.
The links inside those e-mails start from DomainMOD's installation URL. On this image it is always the address DomainMOD is served as (https://<host>): first boot and domainmod-set-hostname (Step 9) keep it in step, so you do not need to edit it by hand. Check it with:
sudo mariadb -N -B domainmod -e "SELECT full_url FROM settings;"
https://ec2-<public-ip-dashed>.compute-1.amazonaws.com
Step 8: The task scheduler
DomainMOD's scheduler processes the domain queue, sends the expiration e-mails, updates currency conversion rates, cleans up, checks for a new DomainMOD release and builds the data warehouse. Upstream asks you to call cron.php every ten minutes; on this image a systemd timer runs it every ten minutes through the PHP command line as the web server user, and cron.php is not reachable over the web:
systemctl is-enabled domainmod-scheduler.timer
systemctl cat domainmod-scheduler.timer | grep -E '^On'
enabled
OnBootSec=5min
OnUnitActiveSec=10min
Turn individual jobs on or off, change their run times or run one now under Administration, Task Scheduler:

The Check For New Version job asks GitHub for the latest DomainMOD release. DomainMOD also reports installs and upgrades to domainmod.org from your first sign in after them; the record the build created was removed, so nothing about the image build is sent from your instance.
Step 9: Your own DNS name and certificate
Point a DNS record at the instance (an Elastic IP keeps the address stable), then set the name DomainMOD is served as. This mints a new self signed certificate for the name, updates nginx, DomainMOD's installation URL and the URL in /root/domainmod-credentials.txt, and keeps the name across reboots:
sudo domainmod-set-hostname <your-domain>
To use a certificate from a public or internal certificate authority instead (for example one issued by Let's Encrypt for <your-domain>), copy it over the generated pair and reload nginx (running domainmod-set-hostname again replaces it with a self signed one):
sudo install -m 0644 <your-domain>.crt /etc/ssl/domainmod/domainmod.crt
sudo install -m 0600 <your-domain>.key /etc/ssl/domainmod/domainmod.key
sudo nginx -t && sudo systemctl reload nginx
Step 10: Security checks
The installer is gone and nginx serves only the application. The configuration file, the vendored code, temp/, the scheduler and the upgrade entry point answer 404, and plain HTTP redirects to HTTPS:
H=$(sudo grep '^DOMAINMOD_URL=' /root/domainmod-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/#\1#')
for p in /index.php /install/ /_includes/config.inc.php /vendor/autoload.php /temp/ /cron.php /update.php; do
curl -sk -o /dev/null -w "%{http_code} $p\n" --resolve "$H:443:127.0.0.1" "https://$H$p"
done
curl -s -o /dev/null -w 'http:// -> %{http_code} %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
ls /var/www/domainmod/install 2>&1 | sed 's/^ls: //'
[ "$(curl -sk -o /dev/null -w '%{http_code}' --resolve "$H:443:127.0.0.1" "https://$H/install/")" = "404" ]
200 /index.php
404 /install/
404 /_includes/config.inc.php
404 /vendor/autoload.php
404 /temp/
404 /cron.php
404 /update.php
http:// -> 301 https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/
cannot access '/var/www/domainmod/install': No such file or directory
admin is the only account, and upstream's admin / admin password hash is nowhere in the database:
sudo mariadb -N -B domainmod -e "SELECT CONCAT('accounts that can sign in: ', GROUP_CONCAT(username)) FROM users; SELECT CONCAT('upstream admin/admin hashes present: ', COUNT(*)) FROM users WHERE password = CONCAT('*', UPPER(SHA1(UNHEX(SHA1('admin')))));"
accounts that can sign in: admin
upstream admin/admin hashes present: 0
DomainMOD reports version 4.23.0 in its code and its database:
echo "$(cat /var/www/domainmod/version.txt)"
sudo mariadb -N -B domainmod -e "SELECT db_version FROM settings;"
4.23.0
4.23.0
The certificate was generated on this instance and names its own addresses:
H=$(sudo grep '^DOMAINMOD_URL=' /root/domainmod-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/#\1#')
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
subject=CN = ec2-<public-ip-dashed>.compute-1.amazonaws.com, O = cloudimg
X509v3 Subject Alternative Name:
DNS:ec2-<public-ip-dashed>.compute-1.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost
None of the services can reach the instance metadata service, so a registrar or data warehouse address pointed at it gets nothing back:
systemctl show nginx php8.3-fpm mariadb domainmod-scheduler domainmod-firstboot -p Id -p IPAddressDeny
IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32
Id=nginx.service
IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32
Id=php8.3-fpm.service
IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32
Id=mariadb.service
IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32
Id=domainmod-scheduler.service
IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32
Id=domainmod-firstboot.service
nginx hands PHP the configured host name and the client address from the connection, never values a client sends:
grep -E 'fastcgi_param (HTTP_HOST|HTTPS|HTTP_X_FORWARDED_FOR|HTTP_X_REAL_IP|HTTP_CLIENT_IP) ' /etc/nginx/sites-available/cloudimg-domainmod
fastcgi_param HTTP_HOST $domainmod_host;
fastcgi_param HTTPS on;
fastcgi_param HTTP_X_FORWARDED_FOR $remote_addr;
fastcgi_param HTTP_X_REAL_IP $remote_addr;
fastcgi_param HTTP_CLIENT_IP $remote_addr;
nginx also refuses a form post whose Origin is another web site (HTTP 403).
Step 11: Backup and maintenance
Your whole estate lives in the MariaDB database domainmod, whose data directory is on the dedicated data volume: /data/mysql is mounted onto /var/lib/mysql.
findmnt -no SOURCE,TARGET,FSTYPE /data
findmnt -no SOURCE,TARGET /var/lib/mysql
/dev/nvme1n1 /data ext4
/dev/nvme1n1[/mysql] /var/lib/mysql
(The device name can differ on your instance.) The simplest backup is an EBS snapshot of the data volume. For a consistent dump while DomainMOD is running:
sudo mariadb-dump --single-transaction --routines domainmod | gzip > <backup-dir>/domainmod-$(date +%F).sql.gz
DomainMOD's own Administration, Maintenance, Backup & Restore page also works on this image: it writes domainmod-backup.sql into /var/www/domainmod/temp/, which the web server never serves (you download it through the signed in page). The dump holds every password hash and any registrar API keys you saved, so delete it when you have your copy:
sudo rm -f /var/www/domainmod/temp/domainmod-backup.sql
To give the database more room, enlarge the data volume in the EC2 console (Volumes, Modify), then grow the file system:
sudo resize2fs "$(findmnt -no SOURCE /data)"
Operating system security updates install automatically; run sudo apt-get update && sudo apt-get upgrade to apply everything else. nginx, PHP-FPM and MariaDB restart cleanly and DomainMOD keeps all of its data.
| Component | Path |
|---|---|
| DomainMOD | /var/www/domainmod |
| Configuration | /var/www/domainmod/_includes/config.inc.php (mode 0640) |
| Database | MariaDB database domainmod (/var/lib/mysql, on the /data volume) |
| nginx site | /etc/nginx/sites-available/cloudimg-domainmod |
| Certificate and key | /etc/ssl/domainmod/ |
| Boot service | /usr/local/sbin/domainmod-firstboot.sh |
| Host name helper | /usr/local/sbin/domainmod-set-hostname |
| Scheduler | domainmod-scheduler.timer (runs /var/www/domainmod/cron.php) |
| Credentials | /root/domainmod-credentials.txt (mode 0600) |
Step 12: Security recommendations
- Allow port 22 from your admin network only, and ports 80 and 443 only from the networks your users work from. Never open 3306.
- Use your own DNS name and a certificate your users' browsers trust (Step 9).
- Give each person their own DomainMOD user under Administration, Users, and change the
adminpassword if it may have been shared. DomainMOD has no lockout after wrong passwords, so keep the instance off the open internet where you can. - Save registrar API credentials only while you need them, as upstream recommends.
- Keep the operating system patched, and snapshot the data volume regularly.
Support and licensing
DomainMOD is open source under the GNU General Public License version 3 or later, created by Greg Chetcuti; there is no per user or per CPU licence fee. The AMI price covers cloudimg's hardening, first boot setup and support.
- Email: support@cloudimg.co.uk
- Website: https://www.cloudimg.co.uk
- Support: 24/7, with a 24 hour response SLA
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.