How to Install OroCRM on Ubuntu 20.04

Last Updated: Fri, Feb 11, 2022
Business Ubuntu

Introduction

OroCRM is enterprise-ready open-source Customer Relationship Management (CRM) software that offers:

  • Out-of-the-box features to meet various business requirements,
  • Flexibility for companies of all sizes, and
  • Extensions to integrate with third-party applications, such as Microsoft 365, Google Data Studio, and MailChimp.

This article walks you through deploying OroCRM Community Edition 4.2.8, the latest stable release of OroCRM Community Edition for now, on a Vultr Ubuntu 20.04 LTS server instance in the production environment.

Prerequisites

  • A newly created Vultr Ubuntu 20.04 LTS server instance with at least 4GB of RAM. Say its IPv4 address is 203.0.113.100.
  • A domain name crm.example.com for accessing the internal CRM system, leaving example.com and www.example.com for the public portal.

1. Setup DNS Records

If you are hosting the apex domain name example.com on Vultr, set up DNS records as follows to bind crm.example.com to your Vultr server's IPv4 address 203.0.113.100:

Type Name Data TTL(seconds) Priority
A crm 203.0.113.100 300
MX example.com 300 10
NS ns1.vultr.com 300
NS ns2.vultr.com 300

Learn more about managing DNS through Vultr in another article on Vultr DNS.

Instructions on setting up DNS records on other platforms may vary.

2. Harden the System

Use your favorite terminal application on your desktop machine, such as the OpenSSH client, to log in to the server instance as root.

On Windows 10 or 11:

PS > ssh -p22 root@203.0.113.100

On a modern Linux or macOS desktop:

$ ssh -p22 root@203.0.113.100

After you log in, execute the tasks listed in this section to harden the system.

Find if any swap space resides on the system:

# swapon -s

If so, feel free to move to the next task. If not, set up a swap file of 2GB as follows:

# fallocate -l 2g /swap
# chmod 600 /swap
# mkswap /swap
# swapon /swap
# echo '/swap    none swap defaults 0 0' >> /etc/fstab
# free -m

Change the preset password of root to a new, private password NewPassword4Root, and then delete the history entry which shows the password in plain text:

# echo 'root:NewPassword4Root' | chpasswd && history -d -1

Create a normal user named orosa with sudo privileges as a designated system administrator, along with its password SudoUserPassword:

# useradd -ms /bin/bash orosa
# echo 'orosa:SudoUserPassword' | chpasswd && history -d -1
# echo 'orosa    ALL=(ALL)   NOPASSWD: ALL' | tee -a /etc/sudoers.d/designated
# chmod 0440 /etc/sudoers.d/designated

Setup firewall rules to allow only SSH, HTTP, and HTTPS traffic:

# ufw default deny
# ufw allow ssh
# ufw allow http
# ufw allow https
# ufw enable
# ufw status

Update and then restart the system:

# apt update
# apt upgrade -y
# apt autoremove -y
# shutdown -r now

Now turn to your desktop machine to generate an SSH key pair consisting of a public key named crm.key.pub and a private key named crm.key, and then upload the public key to the server instance.

On Windows 10 or 11:

PS > ssh-keygen -t rsa -b 4096 -f C:\Users\$env:UserName\.ssh\crm.key -P YourPassphrase -C 'orosa@crm.example.com'
PS > scp C:\Users\$env:UserName\.ssh\crm.key.pub orosa@203.0.113.100:/home/orosa/.ssh/

On a modern Linux or macOS desktop:

$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/crm.key -P YourPassphrase -C 'orosa@crm.example.com'
$ scp ~/.ssh/crm.key.pub orosa@203.0.113.100:/home/orosa/.ssh/

Log in to the server instance as orosa for follow-on tasks. Input the password SudoUserPassword when prompted.

On Windows 10 or 11:

PS > ssh -p22 orosa@203.0.113.100

On a modern Linux or macOS desktop:

$ ssh -p22 orosa@203.0.113.100

Continue setting up key-based SSH authentication:

$ cat /home/orosa/.ssh/crm.key.pub >> /home/orosa/.ssh/authorized_keys
$ chmod 600 /home/orosa/.ssh/authorized_keys
$ rm /home/orosa/.ssh/crm.key.pub

Disallow password authentication for SSH:

$ sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config

As a security routine, prohibit root from remotely logging in through SSH:

$ sudo sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config

Reload the SSH service and then exit the current SSH session:

$ sudo systemctl reload sshd.service
$ exit

From now on, you must log in as orosa using the private key crm.key for system administration. Input the passphrase YourPassphrase when prompted.

On Windows 10 or 11:

PS > ssh -p22 orosa@203.0.113.100 -i C:\Users\$env:UserName\.ssh\crm.key

On a modern Linux or macOS desktop:

$ ssh -p22 orosa@203.0.113.100 -i ~/.ssh/crm.key

3. Install MySQL Community Edition 8.0.28

For OroCRM Community Edition 4.2.8, MySQL 8.0 is the required Database Management System (DBMS). Do not choose MariaDB because OroCRM is not tested with it.

Download the latest MySQL Community Edition 8.0 Advanced Packaging Tool (APT) repository package from the official MySQL APT Repository Download Page:

$ cd
$ wget https://dev.mysql.com/get/mysql-apt-config_0.8.22-1_all.deb

Verify the MD5 checksum of the package:

$ md5sum mysql-apt-config_0.8.22-1_all.deb

If the above command outputs a value different than ade43b291d4b8db2a00e292de7307745, delete the downloaded .deb file and then re-download it.

Setup the MySQL Community Edition 8.0 APT repository:

$ sudo dpkg -i mysql-apt-config_0.8.22-1_all.deb

In the Package configuration window, use UP and DOWN to highlight an option, and then use ENTER to select the highlighted one. The final result should be:

MySQL Server & Cluster (Currently selected: mysql-8.0)
MySQL Tools & Connectors (Currently selected: Enabled)
MySQL Preview Packages (Currently selected: Disabled)
Ok

Highlight the fourth row Ok, and press ENTER to confirm your configuration.

Update the APT repository on the system:

$ sudo apt update

Install the latest stable release of mysql-server 8.0 package:

$ sudo apt install mysql-server -y

During the installation, make sure to input a strong password (say it is @5tr0ngPassw0rd!) for the root account when prompted, and then choose Use Strong Password Encryption (RECOMMENDED) for the default authentication plugin option to improve security.

Confirm the installation of MySQL 8.0:

$ mysql -V
mysql  Ver 8.0.28 for Linux on x86_64 (MySQL Community Server - GPL)

Start the MySQL service:

$ sudo systemctl start mysql.service
$ sudo systemctl enable mysql.service

Run an official script named mysql_secure_installation to secure the installation of MySQL:

$ sudo mysql_secure_installation

When prompted, reply to questions (abridged) as follows:

  • Enter password for user root: @5tr0ngPassw0rd!
  • Would you like to setup VALIDATE PASSWORD component? `y'
  • For password validation policy, please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 2
  • Estimated strength of the password: 100. Change the password for root ? n
  • Remove anonymous users? `y'
  • Disallow root login remotely? `y'
  • Remove test database and access to it? `y'
  • Reload privilege tables now? `y'

Note: Make sure the password you set up reads a 100 strength score.

Because MySQL reads settings from various configuration files, find possible locations of MySQL configuration files as follows:

$ mysql --help | grep "Default options" -A 1
Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf

Create a global MySQL configuration file:

$ sudo nano /etc/my.cnf

Populate the file to optimize MySQL performance:

[client]
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4

[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
optimizer_search_depth = 0

Press CTRL+O, ENTER, and CTRL+X to save and quit.

Restart the MySQL service to load updated configurations:

sudo systemctl restart mysql.service

4. Install PHP 8.0.14

OroCRM Community Edition 4.2.8 works with PHP 7.4.14+ or PHP 8.0.x. This article prefers the latest stable release of PHP 8.0, which is PHP 8.0.14 for now.

Set up a third-party Personal Package Archives (PPA) repository:

$ LC_ALL=C.UTF-8 sudo add-apt-repository ppa:ondrej/php -y
$ sudo apt update
$ sudo apt upgrade -y

Install PHP 8.0 and required PHP 8.0 extensions for OroCRM Community Edition 4.2.8, including the FastCGI Process Manager (FPM) extension for working with Nginx:

$ sudo apt install php8.0 php8.0-bcmath php8.0-common php8.0-curl php8.0-fpm php8.0-gd php8.0-imap php8.0-intl php8.0-ldap php8.0-mbstring php8.0-mysql php8.0-mongodb php8.0-opcache php8.0-soap php8.0-tidy php8.0-xml php8.0-zip -y
$ php -v
PHP 8.0.14 (cli) (built: Dec 20 2021 21:22:57) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.14, Copyright (c) Zend Technologies
with Zend OPcache v8.0.14, Copyright (c), by Zend Technologies

As required by OroCRM Community Edition 4.2.8, change PHP settings for PHP-FPM and PHP-CLI as follows.

Specify the timezone the server is in for both PHP-FPM and PHP-CLI, such as America/Los_Angeles:

$ sudo sed -i 's#;date.timezone =#date.timezone = America/Los_Angeles#' /etc/php/8.0/fpm/php.ini
$ sudo sed -i 's#;date.timezone =#date.timezone = America/Los_Angeles#' /etc/php/8.0/cli/php.ini

For other timezones, refer to the PHP Timezones List page.

Change the memory limit for PHP-FPM to 1Gb:

$ sudo sed -i 's/memory_limit = 128M/memory_limit = 1G/' /etc/php/8.0/fpm/php.ini

Increase execution time to 60 seconds for both PHP-FPM and PHP-CLI:

$ sudo sed -i 's/max_execution_time = 30/max_execution_time = 60/' /etc/php/8.0/fpm/php.ini
$ sudo sed -i 's/max_execution_time = 30/max_execution_time = 60/' /etc/php/8.0/cli/php.ini

Disallow detecting Unicode for both PHP-FPM and PHP-CLI:

$ echo 'detect_unicode = Off' | sudo tee -a /etc/php/8.0/fpm/php.ini
$ echo 'detect_unicode = Off' | sudo tee -a /etc/php/8.0/cli/php.ini

Enable OPcache for PHP-FPM:

$ sudo nano /etc/php/8.0/fpm/php.ini

Press F6 to search for [opcache], and then add the following lines right under the [opcache] line:

opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=512
opcache.max_accelerated_files=65407
opcache.interned_strings_buffer=32
realpath_cache_size=4096K
realpath_cache_ttl=600
opcache.save_comments=1
opcache.validate_timestamps=0

Press CTRL+O, ENTER, and CTRL+X to save and quit.

Disable OPcache for PHP-CLI:

$ sudo nano /etc/php/8.0/cli/php.ini

Press F6 to search for [opcache], and then add the following line right under the [opcache] line:

opcache.enable_cli=0

Press CTRL+O, ENTER, and CTRL+X to save and quit.

Optimize PHP-FPM performance as follows while keeping the default pm = dynamic setting:

$ sudo sed -i 's/pm.max_children = 5/pm.max_children = 128/' /etc/php/8.0/fpm/pool.d/www.conf
$ sudo sed -i 's/pm.start_servers = 2/pm.start_servers = 8/' /etc/php/8.0/fpm/pool.d/www.conf
$ sudo sed -i 's/pm.min_spare_servers = 1/pm.min_spare_servers = 4/' /etc/php/8.0/fpm/pool.d/www.conf
$ sudo sed -i 's/pm.max_spare_servers = 3/pm.max_spare_servers = 8/' /etc/php/8.0/fpm/pool.d/www.conf
$ sudo sed -i 's/;pm.max_requests = 500/pm.max_requests = 512/' /etc/php/8.0/fpm/pool.d/www.conf
$ sudo sed -i 's/;catch_workers_output = yes/catch_workers_output = yes/' /etc/php/8.0/fpm/pool.d/www.conf

Start the PHP-FPM service:

$ sudo systemctl start php8.0-fpm.service
$ sudo systemctl enable php8.0-fpm.service

5. Install Nginx 1.20.2 and its PageSpeed Module

OroCRM Community Edition 4.2.8 needs to work with a web server, such as Apache or Nginx. This article prefers the latest stable release of Nginx, which is Nginx 1.20.2 for now. To increase performance, this article builds both Nginx and its PageSpeed module from source.

Install dependencies for compiling Nginx and the PageSpeed module:

$ sudo apt install git unzip build-essential openssl libssl-dev libpcre3 libpcre3-dev zlib1g zlib1g-dev libgd-dev libxml2 libxml2-dev uuid-dev -y

Compile and install Nginx 1.20.2 and the latest stable release of PageSpeed:

$ bash <(curl -f -L -sS https://ngxpagespeed.com/install) --nginx-version 1.20.2 -a '--prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module' -y

Add the nginx user for running the Nginx service:

$ sudo useradd -M nginx

Make working directories for Nginx:

$ sudo mkdir -p /var/cache/nginx/{client_temp,proxy_temp,fastcgi_temp,uwsgi_temp,scgi_temp}
$ sudo chown -R nginx:nginx /var/cache/nginx
$ sudo mkdir /etc/nginx/{conf.d,sites-available,sites-enabled}
$ sudo mkdir /var/www/
$ sudo mkdir /var/www/crm.example.com
$ sudo chown nginx:nginx /var/www/crm.example.com

Setup a systemd configuration file for Nginx:

$ sudo nano /lib/systemd/system/nginx.service

Populate the file as follows:

[Unit]
Description=nginx - high performance web server
Documentation=https://nginx.org/en/docs/
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/bin/sh -c "/bin/kill -s HUP $(/bin/cat /var/run/nginx.pid)"
ExecStop=/bin/sh -c "/bin/kill -s TERM $(/bin/cat /var/run/nginx.pid)"

[Install]
WantedBy=multi-user.target

Press CTRL+O, ENTER, and CTRL+X to save and quit.

Confirm the installation of Nginx:

$ nginx -V
nginx version: nginx/1.20.2
built by gcc 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)
built with OpenSSL 1.1.1f  31 Mar 2020
TLS SNI support enabled
configure arguments: ...

Start the Nginx service:

$ sudo systemctl start nginx.service
$ sudo systemctl enable nginx.service

Configure and test the installation of the PageSpeed module:

$ sudo mkdir /var/cache/ngx_pagespeed_cache/
$ sudo chown nginx:nginx /var/cache/ngx_pagespeed_cache/

Edit the main Nginx configuration file:

$ sudo nano /etc/nginx/nginx.conf

Insert the following settings into the server{ } segment, under the server_name localhost; line:

pagespeed on;
pagespeed FileCachePath "/var/cache/ngx_pagespeed_cache/";
pagespeed RewriteLevel OptimizeForBandwidth;
pagespeed EnableFilters collapse_whitespace;
pagespeed Disallow "*.svg*";

location ~ "\.pagespeed\.([a-z]\.)?[a-z]{2}\.[^.]{10}\.[^.]+" {
    add_header "" "";
}

location ~ "^/pagespeed_static/" { }
location ~ "^/ngx_pagespeed_beacon$" { }

Press CTRL+O, ENTER, and CTRL+X to save and quit.

Restart the Nginx service:

$ sudo systemctl restart nginx.service

Use the curl utility to confirm the installation of the PageSpeed module:

$ curl -I -X GET http://203.0.113.100
HTTP/1.1 200 OK
Server: nginx/1.20.2
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Date: Sun, 23 Jan 2022 13:42:35 GMT
X-Page-Speed: 1.13.35.2-0
Cache-Control: max-age=0, no-cache

Update PHP-FPM configurations to work with Nginx:

$ sudo sed -i 's/user = www-data/user = nginx/' /etc/php/8.0/fpm/pool.d/www.conf
$ sudo sed -i 's/group = www-data/group = nginx/' /etc/php/8.0/fpm/pool.d/www.conf
$ sudo sed -i 's/listen.owner = www-data/listen.owner = nginx/' /etc/php/8.0/fpm/pool.d/www.conf
$ sudo sed -i 's/listen.group = www-data/listen.group = nginx/' /etc/php/8.0/fpm/pool.d/www.conf
$ sudo systemctl restart php8.0-fpm.service

6. Get Let's Encrypt TLS Certificates

To enable the HTTPS protocol in the production environment, follow the instructions below to get free Let's Encrypt TLS certificates for crm.example.com using the Certbot Automatic Certificate Management Environment (ACME) client.

Update the system-provided snapd program:

$ sudo snap install core; sudo snap refresh core

To avoid potential conflicts, remove any existing Certbot programs installed from other sources:

$ sudo apt-get remove certbot
$ sudo sed -i '/certbot-auto/d' /etc/crontab
$ sudo rm -f /usr/local/bin/cert-bot
$ sudo rm -rf /opt/eff.org

Install Certbot through snapd:

$ sudo snap install --classic certbot

For convenience, create a symbolic link pointing to Certbot in the /usr/bin directory:

$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Get Let's Encrypt certificates for the designated domain name crm.example.com through Certbot and its Nginx plugin:

$ sudo certbot certonly --nginx --email admin@example.com --agree-tos --no-eff-email -d crm.example.com

By default, the Let's Encrypt TLS certificates expire in 90 days, but the Certbot program has setup automatic certificate renewal to prevent that from happening. Use commands below to confirm those renewals are in schedule:

$ sudo certbot renew --dry-run
$ sudo systemctl list-timers | grep snap.certbot.renew.service

7. Enable HTTPS on the Nginx Site

Follow instructions in this section to enable HTTPS on the Nginx site in a production environment, including:

  • Redirecting HTTP traffic to HTTPS, and
  • Forbidding direct access to the server's IPv4 address 203.0.113.100.

Backup the existing Nginx configuration file:

$ sudo mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak

Generate a 2048-bit Diffie-Hellman parameters file for Nginx to enhance security:

$ sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048

Create the main Nginx configuration file:

$ sudo nano /etc/nginx/nginx.conf

Populate the file as follows:

user                 nginx;
pid                  /run/nginx.pid;
worker_processes     auto;
worker_rlimit_nofile 65535;

events {
    multi_accept       on;
    worker_connections 65535;
}

http {
    charset                utf-8;
    sendfile               on;
    tcp_nopush             on;
    tcp_nodelay            on;
    server_tokens          off;
    log_not_found          off;
    types_hash_max_size    2048;
    types_hash_bucket_size 64;
    client_max_body_size   16M;

    # MIME
    include                mime.types;
    default_type           application/octet-stream;

    # Logging
    access_log             /var/log/nginx/access.log;
    error_log              /var/log/nginx/error.log warn;

    # SSL
    ssl_session_timeout    1d;
    ssl_session_cache      shared:SSL:10m;
    ssl_session_tickets    off;

    # Diffie-Hellman parameter for DHE ciphersuites
    ssl_dhparam            /etc/nginx/dhparam.pem;

    # Mozilla Intermediate configuration
    ssl_protocols          TLSv1.2 TLSv1.3;
    ssl_ciphers            ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

    # OCSP Stapling
    ssl_stapling           on;
    ssl_stapling_verify    on;
    resolver               1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s;
    resolver_timeout       2s;

    # Load configs
    include                /etc/nginx/conf.d/*.conf;
    include                /etc/nginx/sites-enabled/*;
}

Press CTRL+O, ENTER, and CTRL+X to save and quit.

Create the Nginx configuration file for crm.example.com:

$ sudo nano /etc/nginx/sites-available/crm.example.com.conf

Populate the file as follows:

server {
    listen                  443 ssl http2;
    listen                  [::]:443 ssl http2;
    server_name             crm.example.com;
    set                     $base /var/www/crm.example.com;
    root                    $base/public;

    #PageSpeed
    pagespeed on;
    pagespeed FileCachePath "/var/cache/ngx_pagespeed_cache/";
    pagespeed RewriteLevel OptimizeForBandwidth;
    pagespeed EnableFilters collapse_whitespace;
    pagespeed Disallow "*.svg*";
    pagespeed SslCertDirectory "/etc/letsencrypt/live/crm.example.com/";
    pagespeed SslCertFile "/etc/letsencrypt/live/crm.example.com/fullchain.pem";
    pagespeed FetchHttps enable;

    location ~ "\.pagespeed\.([a-z]\.)?[a-z]{2}\.[^.]{10}\.[^.]+" {
        add_header "" "";
    }

    location ~ "^/pagespeed_static/" { }
    location ~ "^/ngx_pagespeed_beacon$" { }

    # SSL
    ssl_certificate         /etc/letsencrypt/live/crm.example.com/fullchain.pem;
    ssl_certificate_key     /etc/letsencrypt/live/crm.example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/crm.example.com/chain.pem;

    # . files
    location ~ /\.(?!well-known) {
        deny all;
    }

    # index.php
    index index.php;

    # favicon.ico
    location = /favicon.ico {
        log_not_found off;
        access_log    off;
    }

    # robots.txt
    location = /robots.txt {
        log_not_found off;
        access_log    off;
    }

    # One week for CSS and JavaScript
    location ~* \.(?:css(\.map)?|js(\.map)?)$ {
        expires 1w;
        access_log off;
        add_header Cache-Control public;
    }
    # Three weeks for media
    location ~* \.(?:jpg|jpeg|gif|png|webp|ico|cur|tif|tiff|woff|woff2|heic|eot|ttc|otf|ttf|svg|svgz|mpg|mpeg|mp3|mp4|wmv|avi|mov|m4a|mid|midi|wav|aac|ogg|ogv|webm|swf|flv)$ {
        if ( -f $request_filename ) { expires 3w; add_header "Cache-Control" "public"; }
        try_files $uri /index.php$is_args$args;
        access_log off;
    }

    # gzip
    gzip            on;
    gzip_vary       on;
    gzip_buffers 16 8k;
    gzip_proxied    any;
    gzip_comp_level 5;
    gzip_disable "msie6";
    gzip_min_length 1000;
    gzip_http_version 1.0;
    gzip_types      text/plain text/css text/xml text/javascript application/json application/javascript application/x-javascript application/rss+xml application/atom+xml image/svg+xml;

    try_files $uri $uri/ @rewrite;

    location @rewrite {
        rewrite ^/(.*)$ /index.php/$1;
    }

    location ~ /\.ht {
        deny all;
    }

    # handle .php
    location ~ [^/]\.php(/|$) {
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        if (!-f $document_root$fastcgi_script_name) {
            return 404;
        }
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;

        fastcgi_index                   index.php;
        fastcgi_intercept_errors        on;
        fastcgi_connect_timeout         300;
        fastcgi_send_timeout            300;
        fastcgi_read_timeout            300;

        fastcgi_buffers                 4 256k;
        fastcgi_buffer_size             128k;
        fastcgi_busy_buffers_size       256k;
        fastcgi_temp_file_write_size    256k;

        fastcgi_param DOCUMENT_ROOT     $realpath_root;
        fastcgi_param SCRIPT_FILENAME   $realpath_root$fastcgi_script_name;
        fastcgi_param PATH_INFO         $fastcgi_path_info;
        fastcgi_param HTTPS             on;
        fastcgi_param PHP_ADMIN_VALUE   "open_basedir=$base/:/usr/lib/php/:/tmp/";
    }

    location /ws {
        reset_timedout_connection on;

        # Prevent 502 bad gateway error
        proxy_buffers 8 32k;
        proxy_buffer_size 64k;

        # Redirect all HTTP traffic to localhost:8080;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-NginX-Proxy true;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_pass http://127.0.0.1:8080/;
        proxy_redirect off;
        proxy_read_timeout 86400;

        # Enable WS support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        error_log /var/log/nginx/crm.example.com_wss_error.log;
        access_log /var/log/nginx/crm.example.com_wss_access.log;
    }
}

# Redirect HTTP to HTTPS
server {
    listen      80;
    listen      [::]:80;
    server_name crm.example.com;

    # ACME-challenge
    location ^~ /.well-known/acme-challenge/ {
        root /var/www/_letsencrypt;
    }

    location / {
        return 301 https://crm.example.com$request_uri;
    }
}

# Restrict direct access to server's IPv4 and Ipv6 addresses
server {
    listen      80 default_server;
    listen      [::]:80 default_server;
    server_name "";
    return 444;
}

Press CTRL+O, ENTER, and CTRL+X to save and quit.

Create a symbolic link to enable the configuration file for crm.example.com:

$ sudo ln -s /etc/nginx/sites-available/crm.example.com.conf /etc/nginx/sites-enabled/

Setup a temporary PHP file for test:

$ sudo mkdir -p /var/www/crm.example.com/public
$ sudo chown nginx:nginx /var/www/crm.example.com/public
$ echo '<?php phpinfo();?>' | sudo tee /var/www/crm.example.com/public/index.php
$ sudo chown nginx:nginx /var/www/crm.example.com/public/index.php

Restart Nginx and PHP-FPM to load new settings:

$ sudo systemctl restart nginx.service
$ sudo systemctl restart php8.0-fpm.service

Test updated Nginx configurations:

$ sudo nginx -t

Then point your favorite web browser to http://crm.example.com. The URL should become https://crm.example.com, and the webpage should display all PHP-related information.

Delete the test file and then move on:

$ sudo rm /var/www/crm.example.com/public/index.php
$ sudo rmdir /var/www/crm.example.com/public

8. Install Node.js 14.18.3

OroCRM Community Edition 4.2.8 requires Node.js (>=12.0, <15.0) for JavaScript assets minification and SCSS assets build. Follow the instructions below to install Node.js 14.18.3, the latest stable release of the Node.js 14 series for now.

$ curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash -
$ sudo apt-get install -y nodejs
$ node -v
v14.18.3

9. Install Composer 2.2.5

Use commands listed below to install Composer 2.2.5, the latest stable release of Composer for now:

$ cd
$ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
$ php -r "if (hash_file('sha384', 'composer-setup.php') === '906a84df04cea2aa72f40b5f787e49f22d4c2f19492ac310e8cba5b96ac8b64115ac402c8cd292b8a03482574915d1a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
$ php composer-setup.php
$ php -r "unlink('composer-setup.php');"
$ sudo mv composer.phar /usr/local/bin/composer
$ composer -V
Composer version 2.2.5 2022-01-21 17:25:52

Notice: The above instructions are only suitable for installing Composer 2.2.5. Be sure to get the latest release of Composer by following the instructions on the official Composer Download page.

10. Install Supervisor 4.1.0

OroCRM requires a process managing program, such as Supervisor, to manage its background processes. Use commands below to install Supervisor 4.1.0, the latest stable version of distribution-provided Supervisor:

$ sudo apt install supervisor
$ supervisord -v
4.1.0

11. Install Image Processing Tools for Optimizing Image Size

As required by OroCRM, install two image processing tools, pngquant and jpegoptim, for optimizing image size in storage:

$ sudo apt install pngquant -y
$ sudo apt install jpegoptim -y

12. Install OroCRM Community Edition 4.2.8

To prepare a MySQL database for OroCRM, log in to the MySQL shell as root, typing the password @5tr0ngPassw0rd! when prompted:

$ sudo mysql -u root -p

In the MySQL shell, create a database named orocrm and a database user named orocrm along with a strong password Pa55word40r0crm!:

mysql> CREATE DATABASE orocrm CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
mysql> CREATE USER 'orocrm'@'localhost' IDENTIFIED BY 'Pa55word40r0crm!';
mysql> GRANT ALL PRIVILEGES ON orocrm.* TO 'orocrm'@'localhost';
mysql> FLUSH PRIVILEGES;
mysql> EXIT;

Download the OroCRM Community Edition 4.2.8 files:

$ cd && git clone -b 4.2.8 https://github.com/oroinc/crm-application.git crm.example.com
$ sudo rmdir /var/www/crm.example.com
$ sudo mv /home/orosa/crm.example.com /var/www/
$ sudo chown -R orosa:orosa /var/www/crm.example.com

Use the acl utility to grant proper permissions on the existing and future OroCRM files for both the designated system administrator named orosa and the user of the Nginx service named nginx:

$ sudo apt install acl
$ sudo mkdir /var/www/crm.example.com/var/sessions
$ sudo chown orosa:orosa /var/www/crm.example.com/var/sessions
$ sudo setfacl -dR -m u:nginx:rwX -m u:orosa:rwX /var/www/crm.example.com/var/{sessions,cache,data,logs}
$ sudo setfacl -R -m u:nginx:rwX -m u:orosa:rwX /var/www/crm.example.com/var/{sessions,cache,data,logs}
$ sudo setfacl -dR -m u:nginx:rwX -m u:orosa:rwX /var/www/crm.example.com/public/{media,js}
$ sudo setfacl -R -m u:nginx:rwX -m u:orosa:rwX /var/www/crm.example.com/public/{media,js}

Use Composer to install all dependencies for OroCRM Community Edition in production:

$ cd /var/www/crm.example.com
$ composer install --prefer-dist --no-dev

During the process, input required parameters as follows:

  • database_driver (pdo_mysql): pdo_mysql
  • database_host ('%env(ORO_DB_HOST)%'): 127.0.0.1
  • database_port ('%env(ORO_DB_PORT)%'): 3306
  • database_name ('%env(ORO_DB_NAME)%'): orocrm
  • database_user ('%env(ORO_DB_USER)%'): orocrm
  • database_password ('%env(ORO_DB_PASSWORD)%'): Pa55word40r0crm!
  • database_server_version ('%env(ORO_DB_VERSION)%'): 8.0.28
  • database_driver_options ({ }): ENTER
  • mailer_transport ('%env(ORO_MAILER_DRIVER)%'): ENTER
  • mailer_host ('%env(ORO_MAILER_HOST)%'): ENTER
  • mailer_port ('%env(ORO_MAILER_PORT)%'): ENTER
  • mailer_encryption ('%env(ORO_MAILER_ENCRYPTION)%'): ENTER
  • mailer_user ('%env(ORO_MAILER_USER)%'): ENTER
  • mailer_password ('%env(ORO_MAILER_PASSWORD)%'): ENTER
  • websocket_bind_address (0.0.0.0): 127.0.0.1
  • websocket_bind_port (8080): 8080
  • websocket_frontend_host ('*'): '*'
  • websocket_frontend_port (8080): 443
  • websocket_frontend_path (''): ws
  • websocket_backend_host ('*'): '*'
  • websocket_backend_port (8080): 8080
  • websocket_backend_path (''): ''
  • websocket_backend_transport (tcp): tcp
  • websocket_backend_ssl_context_options ({ }): ENTER
  • web_backend_prefix (''): ''
  • session_handler (session.handler.native_file): ENTER
  • secret ('%env(ORO_SECRET)%'): aiXaefalo8Hadee7zooquiehe2QuenahviekieFeaPa5eij4aithoa8kee7eipoh
  • installed (null): ENTER
  • assets_version (null): ENTER
  • assets_version_strategy (time_hash): ENTER
  • message_queue_transport (dbal): ENTER
  • message_queue_transport_config (null): ENTER
  • deployment_type (null): ENTER
  • liip_imagine.jpegoptim.binary (null): /usr/bin/jpegoptim
  • liip_imagine.pngquant.binary (null): /usr/bin/pngquant
  • env(ORO_DB_HOST) (127.0.0.1): 127.0.0.1
  • env(ORO_DB_PORT) (null): 3306
  • env(ORO_DB_NAME) (oro_crm): orocrm
  • env(ORO_DB_USER) (root): orocrm
  • env(ORO_DB_PASSWORD) (null): Pa55word40r0crm!
  • env(ORO_DB_VERSION) (null): 8.0.28
  • env(ORO_MAILER_DRIVER) (smtp): ENTER
  • env(ORO_MAILER_HOST) (127.0.0.1): ENTER
  • env(ORO_MAILER_PORT) (null): ENTER
  • env(ORO_MAILER_ENCRYPTION) (null): ENTER
  • env(ORO_MAILER_USER) (null): ENTER
  • env(ORO_MAILER_PASSWORD) (null): ENTER
  • env(ORO_SECRET) (ThisTokenIsNotSoSecretChangeIt): aiXaefalo8Hadee7zooquiehe2QuenahviekieFeaPa5eij4aithoa8kee7eipoh

Note:

  1. By pressing ENTER, you choose to use the default value shown in parentheses.
  2. The above demonstration ignores email-related settings because Vultr blocks the SMTP port by default. A rational email sending solution is to work with a third-party email sending vendor.
  3. For the secret entry, you need to input a long and complicated string, such as the 64-bit random string shown above. Use the pwgen utility to generate one of your own:

    $ sudo apt install pwgen
    $ pwgen 64 1
    
  4. If necessary, review or update those parameters by editing a newly-generated .yml configuration file after running the above composer install command. And remember to save the file before you quit if you make any changes.

    $ nano /var/www/crm.example.com/config/parameters.yml
    

Having finished the installation of dependencies, use the command below to optimize the automatic load process of Composer:

$ composer dump-autoload --optimize --no-dev --classmap-authoritative

For security purposes, disable the PHP Phar module before installing and running OroCRM Community Edition in production:

$ sudo phpdismod phar

Note: In the future, if you need to use Composer to install extensions or update OroCRM, enable the PHP Phar module again as follows:

$ sudo phpenmod phar

Set up an environment variable for Symfony:

$ export SYMFONY_ENV=prod
$ echo 'export SYMFONY_ENV=prod' | sudo tee -a /etc/profile

Install OroCRM Community Edition in a production environment:

$ php bin/console oro:install --env=prod --timeout=2000

During the installation, input administrative information as follows:

  • Application URL (http://localhost): https://crm.example.com
  • Organization name (OroCRM): EXAMPLE.COM
  • Username (admin): admin
  • Email: admin@example.com
  • First name: John
  • Last name: Doe
  • Password: (not echo)
  • Formatting Code (en): en
  • Language (en): en
  • Load sample data (y/n): n

After the installation, run the following command to warm up the API documentation cache:

$ php bin/console oro:api:doc:cache:clear

As required by OroCRM, set up a cron job to run the oro:cron program once per minute:

$ crontab -e

When prompted, press ENTER to select the default nano editor. And then add the following entry to the end of the file:

*/1 * * * * php /var/www/crm.example.com/bin/console oro:cron --env=prod > /dev/null

Press CTRL+O, ENTER, and CTRL+X to save and quit.

If necessary, use crontab -l to review the current user's cron jobs.

Besides, use the Supervisor program to execute OroCRM background processes as follows:

$ sudo nano /etc/supervisor/conf.d/orocrm.conf

Populate the file with:

[program:oro_web_socket]
command=php ./bin/console gos:websocket:server --env=prod
numprocs=1
autostart=true
autorestart=true
directory=/var/www/crm.example.com/
user=nginx
redirect_stderr=true

[program:oro_message_consumer]
command=php ./bin/console oro:message-queue:consume --env=prod
process_name=%(program_name)s_%(process_num)02d
numprocs=5
autostart=true
autorestart=true
directory=/var/www/crm.example.com/
user=nginx
redirect_stderr=true

Press CTRL+O, ENTER, and CTRL+X to save and quit.

Start the Supervisor service:

$ sudo systemctl daemon-reload
$ sudo systemctl start supervisor.service
$ sudo systemctl enable supervisor.service

Check if OroCRM processes are running as scheduled:

$ sudo supervisorctl status

If all goes well, you should find similar output shown below:

oro_message_consumer:oro_message_consumer_00   RUNNING   pid 4040, uptime 0:00:34
oro_message_consumer:oro_message_consumer_01   RUNNING   pid 4041, uptime 0:00:34
oro_message_consumer:oro_message_consumer_02   RUNNING   pid 4042, uptime 0:00:34
oro_message_consumer:oro_message_consumer_03   RUNNING   pid 4043, uptime 0:00:34
oro_message_consumer:oro_message_consumer_04   RUNNING   pid 4044, uptime 0:00:34
oro_web_socket                                 RUNNING   pid 4045, uptime 0:00:34

That's all for installing OroCRM Community Edition 4.2.8 on Ubuntu 20.04 LTS. Now it's time to point your favorite web browser to https://crm.example.com to navigate the OroCRM site. Use the administrator's credentials you set up earlier to log in.

Next Steps

Having finished the installation of OroCRM Community Edition, you need to fulfill the contents on the site and consider if it's necessary to integrate with other platforms or services. Use the Oro Go-live Checklist to find more details.

More Information

Want to contribute?

You could earn up to $600 by adding new articles.