Installing Orocommerce Community 4.2 on Ubuntu 20.04 LTS

In this how-to I will do all steps of a “fresh install of Orocommerce 4.2 on Ubuntu 20.04 LTS. You need to be able to administer an Unbutu or other Linux server and be able to use the text editors (vi, nano or what ever you like). If you can’t: STOP HERE and find somebody who can.


Orocommerce is quite a bit resource consuming so I created a “test server” on my VMWare machine with the following settings:

  • 4 Virtual CPUs
  • 4 GB of memory
  • SSD Disks (hardware mirrored)

Installing Ubuntu 20.04 will not be described here. I installed my server “straight forward” with only OpenSSH as extra package.

Create DNS record for your server

First step is to create a DNS record for your server and make sure that the DNS changes are synced to all DNS servers.

Update the Ubuntu server

sudo apt-get update
sudo apt-get -y dist-upgrade --fix-missing
sudo apt-get -y upgrade --fix-missing
sudo apt-get -y autoremove
sudo apt-get clean
sudo shutdown -r now

Set password for root:

sudo passwd
sudo apt -y install zip npm wget curl gcc g++ make jpegoptim pngquant dirmngr apt-transport-https lsb-release ca-certificates 

Install Chrony NTP Deamon

Whenever you do e-commerce it is a good idea to keep the time of your server as close as possible to the real time. Chrony is a NTP (Network Time Protocol) deamon which does that. Facebook uses Chrony for their servers, so why should you not? 🙂

sudo apt -y install chrony

Check wether the chrony deamon is syncing with other NTP servers. After a couple of minutes you want to see a server with a * or ^* in front of it:

chronyc sources

Install nodejs

Orocommerce needs nodejs 12. Because I like my software to run the latest versions, I installed the latest version which is at the time of writing this post version 14.

cd ~ 
curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh

Inspect the contents of the downloaded script with nano (or your preferred text editor):

nano nodesource_setup.sh

When you are satisfied that the script is safe to run, exit your editor, then run the script with sudo:

sudo bash nodesource_setup.sh

The PPA will be added to your configuration and your local package cache will be updated automatically. You can now install the Node.js package in the same way you normally install packages:

sudo apt install nodejs

Verify that you’ve installed the new version by running node with the -v version flag:

node -v

Install some needed packages:

Install MySQL server

To install the MySQL server issue the following command:

sudo apt install mysql-server

It is always a good idea to secure the mysql directly

sudo mysql_secure_installation

The MySQL deamon can valuate password strength.

VALIDATE PASSWORD COMPONENT can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD component?

Press y|Y for Yes, any other key for No:

If you know what your doing and trust the other sysadmins you work with you can safely answer N here.

Now set the MySQL root password and write it down in a secure place or store it in a password manager.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : 

Answer: Y

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : 

Answer: Y

Remove test database and access to it? (Press y|Y for Yes, any other key for No) : 

Answer: Y

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : 

Answer: Y

Configure the MySQL Server

To store supplementary characters (such as 4-byte emojis), configure the options file to use the utf8mb4 character

Add the following code under [client] to /etc/mysql/debian.cnf

[client]
default-character-set = utf8mb4

Add the following code to /etc/mysql/mysql.conf.d/mysql.cnf

[mysql]
default-character-set = utf8mb4

Add the following code to /etc/mysql/mysql.conf.d/mysqld.cnf

[mysqld]
optimizer_search_depth = 0
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
default-authentication-plugin=mysql_native_password

It is recommended to use SSD’s to store the application data in the MySQL 8.X database. However, if you run your server on HDD and not SDD’s you need to also add the following to this file under [mysqld]:

[mysqld]
innodb_file_per_table = 0
wait_timeout = 28800

For the changes to take effect, restart MySQL server by running:

sudo systemctl enable mysql
sudo systemctl restart mysql

Login the MySQL server as root:

sudo mysql -u root -p

Create the MySQL user which will access the database from the website:

mysql> CREATE USER orocomm@localhost IDENTIFIED BY 's0m€$€cretP@$$w0rD';
Query OK, 0 rows affected (0.02 sec)

Store the password in a secured location, for instance a password manager.

Create the database:

mysql> CREATE DATABASE orocomm CHAR SET utf8;
Query OK, 1 row affected, 1 warning (0.02 sec)

Grant access on the database by the user:

mysql> GRANT ALL ON orocomm.* TO orocomm@localhost;
Query OK, 0 rows affected (0.03 sec)

Enable start of the MySQL server at server boot and restart the MySQL server

sudo systemctl enable mysql
sudo systemctl restart mysql

Install Apache Webserver, Certbot and PHP

Install the software:

sudo apt -y install software-properties-common
sudo add-apt-repository -y ppa:ondrej/php
sudo apt update
sudo apt -y upgrade
sudo apt -y install apache2 certbot python3-certbot-apache php7.4 php7.4-fpm php7.4-cli php7.4-pdo php7.4-mysqlnd php7.4-xml php7.4-soap php7.4-gd php7.4-zip php7.4-intl php7.4-mbstring php7.4-opcache php7.4-curl php7.4-bcmath php7.4-ldap php7.4-pgsql php7.4-dev php7.4-imap php7.4-tidy

Enable the some Apache modules:

sudo a2enmod rewrite headers proxy_fcgi setenvif http2

Enable http2

sudo a2enmod http2

Enable the PHP FPM module:

sudo a2enconf php7.4-fpm

Enable the automatic start of both the PHP FPM and Apache webserver:

sudo systemctl enable php7.4-fpm
sudo systemctl enable apache2

Restart PHP and the Apache Webserver:

sudo systemctl restart php7.4-fpm
sudo systemctl restart apache2

Configure PHP

In /etc/php/7.4/fpm/php.ini change the following parameters:

memory_limit = 1024M
realpath_cache_size=4096K 
realpath_cache_ttl=600
opcache.enable=1 
opcache.enable_cli=0 
opcache.memory_consumption=512 
opcache.interned_strings_buffer=32 
opcache.max_accelerated_files=32531 
opcache.save_comments=1 

At the end of this file add

catch_workers_output = yes

For the changes to take effect, restart PHP-FPM by running:

sudo systemctl restart php7.4-fpm

Install Composer v2

Install composer:

cd ~
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && php composer-setup.php
php -r "unlink('composer-setup.php');"
sudo mv composer.phar /usr/bin/composer

Install Docker and Docker Compose

Install docker:

sudo apt -y install docker.io docker-compose
sudo usermod -aG docker $(whoami)
sudo systemctl enable --now docker
sudo systemctl start docker

Install Symfony Server and enable TLS

cd ~
sudo apt -y install libnss3-tools
wget https://get.symfony.com/cli/installer -O - | bash
echo 'PATH="$HOME/.symfony/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
symfony server:ca:install

Install Orocommerce Community using Composer

cd /var/www
sudo mkdir oroshop
sudo chown yourusername. oroshop

Create a project and download the necessary files:

composer create-project oro/commerce-crm-application oroshop

Some important choices have to be made here: You can change the admin URL for example so it is much harder to guess for hackers.

database_driver (pdo_mysql):
database_host ('%env(ORO_DB_HOST)%'): localhost
database_port ('%env(ORO_DB_PORT)%'): 3306
database_name ('%env(ORO_DB_NAME)%'): orocomm
database_user ('%env(ORO_DB_USER)%'): orocomm
database_password ('%env(ORO_DB_PASSWORD)%'): s0m€$€cretP@$$w0rD
database_server_version ('%env(ORO_DB_VERSION)%):
database_driver_options ({ }):
mailer_transport ('%env(ORO_MAILER_DRIVER)%'):
mailer_host ('%env(ORO_MAILER_HOST)%'):
mailer_port ('%env(ORO_MAILER_PORT)%'):
mailer_encryption ('%env(ORO_MAILER_ENCRYPTION)%'):
mailer_user ('%env(ORO_MAILER_USER)%'):
mailer_password ('%env(ORO_MAILER_PASSWORD)%'):
websocket_bind_address (0.0.0.0):
websocket_bind_port (8080):
websocket_frontend_host (''): websocket_frontend_port (8080): websocket_frontend_path (''): websocket_backend_host (''):
websocket_backend_port (8080):
websocket_backend_path (''):
websocket_backend_transport (tcp):
websocket_backend_ssl_context_options ({ }):
web_backend_prefix (/admin): /admin_blurp
session_handler (session.handler.native_file):
secret ('%env(ORO_SECRET)%'):
installed (null):
assets_version (null):
assets_version_strategy (time_hash):
message_queue_transport (dbal):
message_queue_transport_config (null):
enable_price_sharding ('%env(bool:ORO_ENABLE_PRICE_SHARDING)%'):
deployment_type (null):
liip_imagine.jpegoptim.binary (null):
liip_imagine.pngquant.binary (null):
env(ORO_DB_HOST) (127.0.0.1):
env(ORO_DB_PORT) (null): 3306
env(ORO_DB_NAME) (b2b_crm_dev): orocomm
env(ORO_DB_USER) (root): orocomm
env(ORO_DB_PASSWORD) (null): s0m€$€cretP@$$w0rD
env(ORO_DB_VERSION) (null):
env(ORO_MAILER_DRIVER) (sendmail):
env(ORO_MAILER_HOST) (127.0.0.1):
env(ORO_MAILER_PORT) (null):
env(ORO_MAILER_ENCRYPTION) (null):
env(ORO_MAILER_USER) (null):
env(ORO_MAILER_PASSWORD) (null):
env(ORO_SECRET) (ThisTokenIsNotSoSecretChangeIt):
env(ORO_ENABLE_PRICE_SHARDING) ('0'):

Change directory to the oro installation directory

cd oroshop

Run the installer

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

Make sure you type something usefull at the “Application URL” part: If you don’t, you can start all over again

Administration setup.
Application URL (http://localhost): https://shop.yourdomain.com
Organization name (ORO): ABC
Username (admin): thebestadminintheworld
Email: thebestadminintheworld@yourdomain.com
First name: your_firstname
Last name: your_lastname
Password:
Formatting Code (en): 
Language (en):
Load sample data (y/n): y

Warm the API cache

php bin/console oro:api:doc:cache:clear  --env=prod

Configure Apache 2.4

Create a virtual webserver configuration file for Apache:

sudo nano /etc/apache2/sites-available/shop.yourdomain.com.conf

Change the name of the “shop.yourdomain.com” to the domain name of your website

Add the following to this file:

<VirtualHost *:80>
    ServerName shop.yourdomain.com
    #ServerAlias www.<your-domain-name>

    DirectoryIndex index.php
    DocumentRoot /var/www/oroshop/public
    <Directory  /var/www/oroshop/public>
        # enable the .htaccess rewrites
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog /var/log/apache2/shop.yourdomain.com_error.log
    CustomLog /var/log/apache2/shop.yourdomain.com_access.log combined
</VirtualHost>

Replace <application-root-folder> with the absolute path to the Oro application. Replace <your-domain-name> with the configured domain name that is used for the Oro application.

Disable the default site and enable shop.yourdomain.com:

sudo a2dissite 000-default
sudo a2ensite shop.yourdomain.com

Reload apache:

sudo systemctl reload apache2

Have a look at the log file to check wether there are any errors reloading apache2

tail /var/log/syslog

Get the SSL certificate

I use the free Let’s Encrypt SSL cetrificates for my servers.

sudo certbot --apache

Go through all the steps…

sudo systemctl restart apache2

Have a look at the syslog to double check that Apache starts normally:

 tail /var/log/syslog 

Change the permissions and ownership

Set the correct ownership and permissions:

sudo chown -R www-data. /var/www/oroshop

Set up cron

Orocommerce needs a cron job to be fired every minute. To schedule execution of the oro:cron command every-minute,

sudo -u www-data -e

add the following line to crontab file:

*/1 * * * * php /var/www/oroshop/bin/console oro:cron --env=prod > /dev/null

If you installed orocommerce in an other locat replace with an absolute path to the installed Oro application.

Install Supervisord

Install supervisord:

sudo apt -y install supervisor

Create a configuration file:

sudo nano /etc/supervisor/conf.d/oroshop.conf

Add the following to this file:

[program:oro_web_socket]
command=php ./bin/console gos:websocket:server --env=prod
numprocs=1
autostart=true
autorestart=true
directory=/var/www/oroshop
user=www-data
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/oroshop
user=www-data
redirect_stderr=true

Enable supervisord at boot and restart supervisord

sudo systemctl enable supervisor
sudo systemctl restart supervisor

Now watch supervisord for a couple of minutes by repeating:

sudo supervisorctl status

This is what you want to see after a couple of minutes:

$ sudo supervisorctl status
oro_message_consumer:oro_message_consumer_00 RUNNING pid 54342, uptime 0:03:31
oro_message_consumer:oro_message_consumer_01 RUNNING pid 54343, uptime 0:03:31
oro_message_consumer:oro_message_consumer_02 RUNNING pid 54344, uptime 0:03:31
oro_message_consumer:oro_message_consumer_03 RUNNING pid 54345, uptime 0:03:31
oro_message_consumer:oro_message_consumer_04 RUNNING pid 54346, uptime 0:03:31
oro_web_socket RUNNING pid 54347, uptime 0:03:31

If your message consumers keep restarting and you see errors like below, change “numprocs=5” to “numprocs=1” and retart supervisord

1213 Deadlock found when trying to get lock;
[2021-05-11 02:56:42] console.ERROR: Error thrown while running command "oro:message-queue:consume --env=prod". Message: "An exception occurred while executing 'INSERT IGNORE INTO oro_message_queue_job_unique(name) VALUES(?);' with params ["oro:cron:run_command:oro:cron:imap-sync"]: SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction" {"exception":"[object] (Doctrine\DBAL\Exception\DeadlockException(code: 0): An exception occurred while executing 'INSERT IGNORE INTO oro_message_queue_job_unique(name) VALUES(?);' with params [\"oro:cron:run_command:oro:cron:imap-sync\"]:\n\nSQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction at /var/www/oroshop/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php:51, Doctrine\DBAL\Driver\PDO\Exception(code: 40001): SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction at /var/www/oroshop/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDO/Exception.php:18, PDOException(code: 40001): SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction at /var/www/oroshop/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:115)","command":"oro:message-queue:consume --env=prod","message":"An exception occurred while executing 'INSERT IGNORE INTO oro_message_queue_job_unique(name) VALUES(?);' with params [\"oro:cron:run_command:oro:cron:imap-sync\"]:\n\nSQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction"} []