Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jeanpauldejong/d88f3499c36bd26baec90fe62948f9bb to your computer and use it in GitHub Desktop.
Save jeanpauldejong/d88f3499c36bd26baec90fe62948f9bb to your computer and use it in GitHub Desktop.
Mautic on Ubuntu 24.04 with Nginx, MariaDB, PHP8.3 and composer.

Install Mautic on Ubuntu 24.04 LEMP Server

This guide assumes you have provisioned your server with a LEMP stack (Nginx, MariaDB, PHP 8.3) using cloud-init-lemp.yml. It covers secure MariaDB setup, Composer installation, Mautic deployment, and HTTPS configuration for mautic.example.com.

1. Secure MariaDB and Create Database/User

Set your passwords and database info as needed. Replace all placeholders!

# Secure MariaDB (set root password, remove test DB, etc.)
sudo mysql_secure_installation

# Login as root
sudo mysql -u root -p

# In the MariaDB shell, run:
CREATE DATABASE mautic_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'mautic_user'@'localhost' IDENTIFIED BY 'REPLACE_WITH_STRONG_PASSWORD';
GRANT ALL PRIVILEGES ON mautic_db.* TO 'mautic_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

2. Install Composer, Node.js, npm, and Certbot

sudo apt update
# Install PHP extensions and dependencies
sudo apt install -y curl unzip php-cli php-mbstring php-xml php-curl php-zip php-intl php-gd php-imap php-bcmath php-ldap php-soap php-xmlrpc php-mysql

# Install Node.js 20.x and npm (recommended for Mautic 5.x)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

# Install Certbot for Let's Encrypt SSL certificates
sudo apt install -y certbot

# (Optional) Check versions
node -v
npm -v
certbot --version

# Install Composer
cd ~
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
composer --version

2a. Increase PHP Memory Limit (Recommended for Mautic)

Mautic recommends setting memory_limit to at least 512M for best performance. Edit your PHP configuration:

sudo sed -i 's/^memory_limit = .*/memory_limit = 512M/' /etc/php/8.3/fpm/php.ini
sudo systemctl reload php8.3-fpm

You can verify the new setting with:

php -i | grep memory_limit

Note: The above command updates the memory limit for PHP-FPM (used by Nginx/web). If you check with php -i on the command line, it shows the CLI setting, which may differ. To verify the memory limit for web requests, create a file called phpinfo.php in your web root (/var/www/mautic/docroot):

<?php phpinfo();

Then visit https://yourdomain/phpinfo.php in your browser and search for memory_limit to confirm it is set to 512M.

3. Download and Install Mautic

sudo mkdir -p /var/www/mautic
sudo chown $USER:www-data /var/www/mautic
cd /var/www/mautic

# Download latest Mautic via Composer
composer create-project mautic/recommended-project .

# To install a specific version (e.g., 5.x), use:
# composer create-project mautic/recommended-project:~5.0 .

# Set permissions
sudo chown -R www-data:www-data /var/www/mautic
sudo find /var/www/mautic -type f -exec chmod 644 {} \;
sudo find /var/www/mautic -type d -exec chmod 755 {} \;

4. Configure Nginx for Mautic

Create /etc/nginx/sites-available/mautic.example.com:

server {
    listen 80;
    listen [::]:80;
    server_name mautic.example.com;
    root /var/www/mautic/docroot;

    location ^~ /.well-known/acme-challenge/ {
        allow all;
        default_type "text/plain";
    }
}

Enable the site and reload Nginx:

sudo ln -s /etc/nginx/sites-available/mautic.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

5. Obtain Let’s Encrypt Certificate (Webroot)

sudo certbot certonly --webroot -w /var/www/mautic/docroot -d mautic.example.com

6. Update your Nginx config for SSL

Edit /etc/nginx/sites-available/mautic.example.com and add:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name mautic.example.com;
    root /var/www/mautic/docroot;

    ssl_certificate /etc/letsencrypt/live/mautic.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mautic.example.com/privkey.pem;

    # TLS hardening
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 1.1.1.1 8.8.8.8 valid=300s;
    resolver_timeout 5s;

    # Security Headers
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
    add_header Content-Security-Policy "default-src * 'unsafe-inline' 'unsafe-eval' data: blob:;" always;

    index index.php index.html;
    client_max_body_size 20M;

    location ^~ /.well-known/acme-challenge/ {
        allow all;
        default_type "text/plain";
    }

    location / {
        try_files $uri /index.php$is_args$args;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~* ^/index.php {
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        include snippets/fastcgi-php.conf;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }

    access_log /var/log/nginx/mautic_access.log;
    error_log /var/log/nginx/mautic_error.log;
}

Redirect HTTP to HTTPS:

server {
    listen 80;
    listen [::]:80;
    server_name mautic.example.com;
    root /var/www/mautic/docroot;
    location ^~ /.well-known/acme-challenge/ {
        allow all;
        default_type "text/plain";
    }
    return 301 https://$host$request_uri;
}

Reload Nginx:

sudo nginx -t
sudo systemctl reload nginx

7. Initial Mautic Web Configuration

  • Visit https://mautic.example.com in your browser.
  • Complete the web installer:
    • Database:
      • Host: localhost
      • Database: mautic_db
      • User: mautic_user
      • Password: (your password)
    • Admin user: Set your admin credentials.
    • Email settings: Configure as needed.

8. Test Certificate Renewal

sudo certbot renew --dry-run

9. Mautic CRON jobs

There are a number of cron jobs that should run to benefit the system.

Create /etc/cron.d/mautic

# === MAUTIC CRON JOBS ===

# Update segments every 15 minutes
0,15,30,45 * * * * www-data php /var/www/mautic/bin/console mautic:segments:update

# Update campaigns every 15 minutes, staggered
5,20,35,50 * * * * www-data php /var/www/mautic/bin/console mautic:campaigns:update

# Trigger campaign events every 15 minutes, staggered
10,25,40,55 * * * * www-data php /var/www/mautic/bin/console mautic:campaigns:trigger

# Process email queue every 5 minutes
# NOTE: Only when you install the mail queue option on Mautic
#*/5 * * * * www-data php /var/www/mautic/bin/console mautic:emails:send

# Update MaxMind GeoIP database monthly (first Tuesday of the month at 03:00)
# NOTE: Only if you use Maxmind GeoIP lookup
#0 3 * * 2 [ $(date +\%e) -le 7 ] && www-data php /var/www/mautic/bin/console mautic:iplookup:download

# GDPR Cleanup: delete contacts inactive for 3 years, monthly
0 4 1 * * www-data php /var/www/mautic/bin/console mautic:maintenance:cleanup --gdpr

# MaxMind CCPA Do Not Sell list (weekly)
0 4 * * 1 www-data php /var/www/mautic/bin/console mautic:donotsell:download
10 4 * * 1 www-data php /var/www/mautic/bin/console mautic:max-mind:purge

Ensure correct permissions:

sudo chmod 644 /etc/cron.d/mautic
sudo chown root:root /etc/cron.d/mautic

Confirm the cron service picks it up:

sudo systemctl restart cron

Check it is scheduled:

sudo grep mautic /var/log/syslog | tail -n 50

Optional installs

Maxmind GeoIP lookup

You have to decide for yourself how to do this in a compliant manner. But if you do, this is how you get it to work with Mautic.

Create an account on Maxmind, setup a license key, and download the Geolite2-City file. Then copy this to your instance, e.g.

scp GeoLite2-City_20250704.tar.gz [email protected]:~

Extract and move the file.

tar -xvzf GeoLite2-City_20250704.tar.gz
mkdir -p /var/www/mautic/var/cache/ip_data
mv ~/GeoLite2-City_20250704.tar.gz /var/www/mautic/var/cache/ip_data
chown -R www-data:www-data /var/www/mautic/var/cache/ip_data/GeoLite2-City.mmdb
chmod 644 /var/www/mautic/var/cache/ip_data/GeoLite2-City.mmdb

You may want to restart and clear the cache.

sudo systemctl restart php8.3-fpm
cd /var/www/mautic
sudo -u www-data php bin/console cache:clear

NOTE Don't forget to configure the online service and set the credentials in the format account:secret.

To verify you can check a download from the commandline.

cd /var/www/mautic
sudo -u www-data php bin/console mautic:iplookup:download

You should see Success!, and the timestamp updated.

Notes

  • Replace all placeholders (passwords, domain names) before use.
  • For production, consider further hardening (fail2ban, firewall, etc.).
  • For large installations, review Mautic server requirements.

References:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment