Applications AWS

EspoCRM 10.0.3 on AWS User Guide

| Product: EspoCRM 10.0.3 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the EspoCRM 10.0.3 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg ERP and CRM listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

EspoCRM is an open source customer relationship management application. It brings accounts, contacts, leads, opportunities, cases, emails, meetings, calls, tasks, a calendar, documents, a knowledge base and an activity stream together in one web application, with role based access control, customer portals, import and export, workflows through scheduled jobs and a full REST API.

What the cloudimg image adds:

  • No default login. The administrator password, the MySQL password of the application's database user and EspoCRM's application keys (cryptKey, hashSecretKey) are generated on your instance's first boot and written to /root/espocrm-credentials.txt (root only). The image ships no known password, and EspoCRM refuses blank and common passwords such as admin and password.
  • No install wizard. EspoCRM is installed and its database built from the command line; the web install wizard is deleted from disk and its URL answers 404, so nobody who reaches the instance first can take it over.
  • HTTPS from the first boot. nginx serves EspoCRM on port 443 with a certificate generated on your instance, port 80 redirects to HTTPS, and requests for a host name the instance does not serve are dropped.
  • Fail closed. nginx, PHP-FPM and the scheduled jobs start only after first boot has finished. If it cannot finish, nothing is served.
  • A hardened MySQL. MySQL 8.0 listens on 127.0.0.1 only, with the X protocol, LOAD DATA LOCAL, the binary log and the general query log switched off. MySQL's root account works only from the instance itself (sudo mysql), and there is no maintenance account with a stored password.
  • Hardened defaults. nginx, PHP-FPM, MySQL, the scheduled jobs and the first boot service are blocked from the instance metadata service, the AMI requires IMDSv2, and EspoCRM is handed the configured host name and the real client address from the connection (never values a client sends).
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: EspoCRM 10.0.3 (the upstream release, checksum verified), nginx 1.24, PHP 8.3 FPM with OPcache, MySQL 8.0 (all from Ubuntu 24.04), cloud-init and the AWS Systems Manager agent.

Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large

The EspoCRM leads list with three leads, their status and creation time

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 (EspoCRM) and TCP 80 (redirects to HTTPS) from the networks your users work from. Never open 3306: MySQL listens on the loopback address only.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick EspoCRM 10.0.3 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 root volume holds the database and uploaded files, so give it room to grow (for example 30 GiB or more for a busy CRM).

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> \
  --block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=30,VolumeType=gp3}' \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=espocrm}]'

First boot takes well under a minute after the instance is running.

Step 2: Connect to your instance

Version SSH user
EspoCRM 10.0.3 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 3: Check the services

First boot sets the passwords, keys and certificate, then nginx, PHP-FPM and the scheduled jobs start:

systemctl is-active nginx php8.3-fpm mysql espocrm-cron.timer espocrm-firstboot
sudo test -f /var/lib/cloudimg/espocrm-firstboot.done && echo FIRSTBOOT_DONE

All five units are active and the first boot marker is present:

active
active
active
active
active
FIRSTBOOT_DONE

Only SSH and nginx listen on network ports; MySQL 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' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort
0.0.0.0:22 users:(("sshd"),("systemd"))
0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
127.0.0.1:3306 users:(("mysqld"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))

Step 4: Get your URL and password

sudo cat /root/espocrm-credentials.txt

The file is readable by root only:

# EspoCRM 10.0.3 - generated on first boot by espocrm-firstboot.service
# These credentials are unique to this instance; none was baked into the image. Keep this file secret.

espocrm.url=https://<public-ip>/
espocrm.admin.user=admin
espocrm.admin.pass=<ESPOCRM_ADMIN_PASSWORD>
espocrm.db.name=espocrm
espocrm.db.user=espocrm
espocrm.db.pass=<ESPOCRM_DB_PASSWORD>

# The certificate is self signed (minted on this instance): accept the browser warning once, or install
# your own. Port 80 redirects to https. Change the name EspoCRM is served as (also its siteUrl):
#   sudo espocrm-set-hostname <your-dns-name>
# MySQL listens on 127.0.0.1:3306 only; on the instance 'sudo mysql' is root over the unix socket.
# EspoCRM CLI:  sudo -u www-data php /var/www/espocrm/command.php <command>
# User guide: https://www.cloudimg.co.uk/guides/espocrm-10-0-3-on-ubuntu-24-04-aws/

The URL uses the instance's public IP address from the EC2 instance metadata. The certificate also names the public DNS name and the private IP address, so https://<public-dns-name>/ works too. If you stop and start the instance without an Elastic IP, the boot service picks up the new address, issues a new certificate for it and updates EspoCRM's site URL. To serve EspoCRM under your own DNS name, see Step 7.

Step 5: Sign in

Browse to the URL from the credentials file. Your browser warns once about the self signed certificate; accept it (or install your own certificate, Step 7). Sign in as admin with the password from the credentials file.

The EspoCRM sign in page with the Username and Password fields and the Log in button

After signing in you land on the home dashboard, with the navigation for accounts, contacts, leads, opportunities, activities and support on the left. Add your own users under Administration > Users and sign in as them for day to day work.

The EspoCRM home dashboard with the Stream and My Activities dashlets and the navigation menu

Step 6: Work with your customer records

Choose Leads > Create Lead, fill in the name, account, status and source, and save. Every list supports search, filters, mass actions and export, and a lead converts into an account, a contact and an opportunity in one action.

A lead detail view with the Overview and Details panels, the assigned user, activities, history, tasks and the stream

Administration is where you set the system settings, outbound email (SMTP), authentication options, roles and teams, scheduled jobs, currencies and extensions. Set up outbound email first so password recovery and notifications can reach your users.

The EspoCRM Administration page with the System and Users sections

The scheduled jobs (email sending and fetching, reminders, cleanup) run every minute from espocrm-cron.timer as the www-data user, so leave that timer enabled.

Step 7: Your own DNS name and certificate

Point a DNS record at the instance (an Elastic IP keeps the address stable), then set the name EspoCRM is served as. This mints a new self signed certificate for the name, updates nginx, EspoCRM's site URL (used in the links in emails) and the URL in /root/espocrm-credentials.txt, and keeps the name across reboots:

sudo espocrm-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 espocrm-set-hostname again replaces it with a self signed one):

sudo install -m 0644 <your-domain>.crt /etc/nginx/ssl/espocrm.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/ssl/espocrm.key
sudo nginx -t && sudo systemctl reload nginx

Step 8: Security checks

The shipped round trip signs in as the administrator with this instance's password, proves a wrong, blank, admin and password password are refused, creates, reads and deletes a lead, and checks that MySQL accepts the application user's password and refuses a wrong one. Credentials are handed to curl and mysql in root only temporary files, never on a command line:

sudo bash /usr/local/sbin/espocrm-cred-roundtrip.sh
OK per-instance admin signs in over HTTPS (200); wrong, blank, 'admin' and 'password' refused (401); Lead create/read/delete; MySQL app user: right password in, wrong refused

The install wizard is not on disk and its URL answers 404, the API refuses requests without a login, plain HTTP redirects to HTTPS on the configured name, and a host name the instance does not serve gets no answer at all:

H=$(sudo grep '^espocrm.url=' /root/espocrm-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/#\1#')
for p in / /api/v1/App/user /install/ /install/index.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/"
curl -sk -o /dev/null -w 'unknown host name -> %{http_code}\n' --resolve "other.example:443:127.0.0.1" https://other.example/ || true
test -e /var/www/espocrm/install || echo "install wizard: not on disk"
200  /
401  /api/v1/App/user
404  /install/
404  /install/index.php
http:// -> 301 https://<public-ip>/
unknown host name -> 000
install wizard: not on disk

The certificate was generated on this instance and names its own addresses:

H=$(sudo grep '^espocrm.url=' /root/espocrm-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 = <public-ip>, O = cloudimg
X509v3 Subject Alternative Name:
    IP Address:<public-ip>, DNS:<public-dns-name>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost

None of the services can reach the instance metadata service:

systemctl show nginx php8.3-fpm mysql espocrm-cron espocrm-firstboot -p Id -p IPAddressDeny
IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
Id=nginx.service

IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
Id=php8.3-fpm.service

IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
Id=mysql.service

IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
Id=espocrm-cron.service

IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
Id=espocrm-firstboot.service

nginx hands PHP the configured host name and the client address from the connection, never values a client sends, so EspoCRM's login history records the real address of every sign in:

grep -E 'fastcgi_param (HTTP_HOST|HTTPS|HTTP_X_FORWARDED_FOR|HTTP_X_REAL_IP|HTTP_FORWARDED) ' /etc/nginx/cloudimg-espocrm-fastcgi.conf
fastcgi_param HTTP_HOST $espo_host;
fastcgi_param HTTPS on;
fastcgi_param HTTP_X_FORWARDED_FOR $remote_addr;
fastcgi_param HTTP_X_REAL_IP $remote_addr;
fastcgi_param HTTP_FORWARDED "";

MySQL runs with the cloudimg settings, and its only accounts are the application user (local only) and root over the unix socket:

sudo mysql -e "SELECT @@bind_address, @@local_infile, @@log_bin, @@general_log; SELECT user, host, plugin FROM mysql.user WHERE user NOT LIKE 'mysql.%' ORDER BY user, host;"
cat /etc/cloudimg/espocrm-release.txt
@@bind_address  @@local_infile  @@log_bin   @@general_log
127.0.0.1   0   0   0
user    host    plugin
espocrm 127.0.0.1   caching_sha2_password
espocrm localhost   caching_sha2_password
root    localhost   auth_socket
cloudimg: EspoCRM 10.0.3 (AGPL-3.0-or-later) from github.com/espocrm/espocrm releases, sha256 73369bb3e319846c99f1b6d0254fac9339bbfe46705d6dd882eaf648f85b87b1; install wizard removed.

Step 9: Versions and the command line

sudo -u www-data php /var/www/espocrm/command.php version
php -r 'echo PHP_VERSION."\n";'
mysqld --version
nginx -v
10.0.3
8.3.6
/usr/sbin/mysqld  Ver 8.0.46-0ubuntu0.24.04.4 for Linux on x86_64 ((Ubuntu))
nginx version: nginx/1.24.0 (Ubuntu)

EspoCRM's console runs as the www-data user, for example sudo -u www-data php /var/www/espocrm/command.php clear-cache, rebuild after a configuration change, or app-check for a health check.

Step 10: Changing passwords

  • EspoCRM admin: in EspoCRM open your own user record (your name in the top right menu) and choose Change Password from its menu, or run the command below on the instance; it asks for the new password at a prompt, so the password never appears on a command line. Then update or delete espocrm.admin.pass in /root/espocrm-credentials.txt:
sudo -u www-data php /var/www/espocrm/command.php set-password admin
  • Other users reset their own password from the sign in page once outbound email is set up (Administration > Outbound Emails); password recovery links always point at the configured site URL, never a host name taken from the request.

Step 11: Backup and maintenance

The database lives in MySQL (/var/lib/mysql) and uploaded files under /var/www/espocrm/data/upload. The simplest backup is an EBS snapshot of the root volume. For a logical backup of the database while EspoCRM is running:

sudo mysqldump --single-transaction --routines --triggers espocrm | gzip > <backup-dir>/espocrm-$(date +%F).sql.gz
sudo tar -czf <backup-dir>/espocrm-files-$(date +%F).tar.gz -C /var/www/espocrm data/upload custom

Operating system, nginx, PHP and MySQL security updates install automatically from Ubuntu; run sudo apt-get update && sudo apt-get upgrade to apply everything else. EspoCRM itself upgrades from Administration > Upgrade or with sudo -u www-data php /var/www/espocrm/command.php upgrade; take a snapshot first.

Component Path
EspoCRM /var/www/espocrm (web root /var/www/espocrm/public)
EspoCRM configuration /var/www/espocrm/data/config.php, config-internal.php (www-data only)
MySQL settings /etc/mysql/mysql.conf.d/99-cloudimg-espocrm.cnf
nginx site /etc/nginx/sites-available/cloudimg-espocrm
Certificate and key /etc/nginx/ssl/
Scheduled jobs espocrm-cron.timer / espocrm-cron.service
Boot service /usr/local/sbin/espocrm-firstboot.sh
Host name helper /usr/local/sbin/espocrm-set-hostname
Credentials /root/espocrm-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 to the internet.
  • Use your own DNS name and a certificate your browsers trust (Step 7).
  • Create a named user for each person with a role that limits what they can see, and keep admin for administration.
  • Turn on two factor authentication under Administration > Authentication, change the admin password if it may have been shared (Step 10), keep the operating system patched, and snapshot the volume regularly.

Support and licensing

EspoCRM is open source software licensed under the GNU Affero General Public License version 3 or later, with the additional term that its user interface keeps showing the "EspoCRM" name; cloudimg ships it unmodified. There is no per user licence fee. The AMI price covers cloudimg's hardening, first boot setup and support. EspoCRM is a trademark of its owner; cloudimg is not affiliated with or endorsed by EspoCRM, Inc.

  • Email: support@cloudimg.co.uk
  • Website: https://www.cloudimg.co.uk
  • Support: 24/7, with a 24 hour response SLA