In this post, I am going to guide you through the installation of SuiteCRM in a Docker container in a few simple steps. This will allow you to set up a powerful and fully operational CRM system in less than an hour.
The benefits of Docker installation are numerous:
- The environment is controlled, with the correct version of PHP and the required dependencies.
- You can set this up on any server in just a few minutes.
- Simply add a MariaDB Docker to provide a database (see
compose.yaml
file below), and you will have a functional system. - It is easy to install other Docker containers on the same local network, with applications that can interact with SuiteCRM, such as n8n, Mautic, custom applications, etc. This way, you can build a fully customized, completely secure and very affordable marketing stack.
- If necessary, the Docker container can be moved to another server in just a few minutes.
It is probably better if you have a basic understanding of Docker, mounting volumes, and creating configuration files before you start though.
In my case, the Docker containers serving web applications are running behind a Traefik reverse proxy. If you prefer another setup, you need to modify the docker compose file accordingly.
There may be slight variations in parameters depending on your installation. Anyway, this configuration is quite robust and has been working in production for me for several years.
SuiteCRM Version and Setup
This Docker setup allows you to install SuiteCRM 7.x or 8.x. At the time of writing (early 2024), I am still testing the migration of my production installations from version 7.14.2 to version 8.5.
The setup provides the environment, and you need to download the version that suits you and install the files in the volume mounted by the docker-compose file.
SuiteCRM API v8
One of the major strengths of SuiteCRM is its API, which allows other applications to interact with SuiteCRM. For example:
- Creating or synchronizing contacts or other records from Woo, WordPress, or other web applications.
- Automatically adding notes or documents to contacts.
- Extracting data for processing by other applications.
Each time the Docker starts, script entrypoint.sh
file checks that the SSL keys for the API are present. If not, they are created automatically. This Docker is ready for interactivity!
Files to Create
Here is the structure tree for the files that are used to build the Docker (feel free to adapt the Dockerfile based on where you place the different files):
. ├── Dockerfile └── conf ├── apache │ ├── apache.conf │ └── vhost.conf ├── cron-suitecrm ├── entrypoint.sh └── php ├── php.conf └── php.ini
Dockerfile
This Dockerfile
is based on a designated version of PHP, so it is consistent with the compatibility matrix of SuiteCRM version 7.x and version 8.x. It includes an Apache server that will serve the web application.
FROM php:8.2-apache LABEL vendor="The Moto Company / SL Data Tech" LABEL maintainer="Steph Legrand" # define timezone ENV TZ=Europe/Paris RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone EXPOSE 80 RUN apt-get update && apt-get install -y \ cron \ openssl \ unzip \ zip # Install additional dependencies RUN apt-get update && apt-get install -y \ libicu-dev \ libcurl4-openssl-dev \ libmagickwand-dev \ libpng-dev \ libzip-dev \ libxml2-dev \ libbz2-dev \ libonig-dev \ libgmp-dev \ libldb-dev \ libldap2-dev \ libc-client-dev \ libkrb5-dev \ && rm -rf /var/lib/apt/lists/* # Install PHP extensions RUN docker-php-ext-configure imap --with-kerberos --with-imap-ssl \ && docker-php-ext-install \ pdo_mysql \ gd \ curl \ zip \ xml \ mbstring \ bz2 \ intl \ gmp \ opcache \ soap \ imap \ ldap \ mysqli # apache config COPY conf/apache/apache.conf /etc/apache2/conf-available/suitecrm.conf COPY conf/apache/vhost.conf /etc/apache2/sites-available/suitecrm.conf RUN a2enmod rewrite remoteip RUN a2dissite 000-default \ && a2ensite suitecrm \ && a2enconf suitecrm RUN ln -sf /proc/self/fd/1 /var/log/apache2/access.log && \ ln -sf /proc/self/fd/1 /var/log/apache2/error.log # php config COPY conf/php/php.ini /usr/local/etc/php/conf.d/suitecrm.ini # cron config COPY conf/cron-suitecrm /etc/cron.d/suitecrm # entrypoint config COPY conf/entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh WORKDIR /app/suitecrm ENTRYPOINT [ "/entrypoint.sh" ] CMD ["apache2-foreground"]
File entrypoint.sh
Next is the entrypoint.sh
script. It is executed each time the Docker starts. In this specific case, its role is to:
- Check the SuiteCRM version and apply the correct permissions to directories and files.
- Change the base directory for Apache, depending on SuiteCRM version.
- Generate SSL keys for OAuth2 if needed (to enable API v8) and check permissions.
- Finally, start the Apache server.
Some may find it excessive to execute this process every time. You can modify this file as you wish. For me, it ensures that changes made to custom modules (especially some PHP scripts) have the correct permissions.
#!/bin/bash if [ "$1" = 'apache2-foreground' ]; then # this is run only for main docker execution echo "[ tmc ] This is a SuiteCRM docker by The Moto Company / SL Daata Tech" > /dev/stdout if [ -f "/app/suitecrm/public/index.php" ]; then # Scenario 1: SuiteCRM 8 - /app/suitecrm/public/index.php exists echo "[ tmc ] running SuiteCRM 8 configuration" > /dev/stdout api_root_path="/app/suitecrm/public/legacy/Api" echo "[ tmc ] api root path set to " $api_root_path > /dev/stdout # web server root is /app/suitecrm/public # nothing more to do in apache conf files echo "[ tmc ] web server root set " > /dev/stdout # set permissions for SuiteCRM 8 directory structure echo "[ tmc ] setting permissions for SuiteCRM 8 directories..." > /dev/stdout cd /app/suitecrm find . -type d -not -perm 2755 -exec chmod 2755 {} \; find . -type f -not -perm 0644 -exec chmod 0644 {} \; find . ! -user www-data -exec chown www-data:www-data {} \; chmod +x bin/console echo "[ tmc ] done." > /dev/stdout else # Scenario 2: SuiteCRM 7 - /app/suitecrm/public/index.php does not exist echo "[ tmc ] running SuiteCRM 7 configuration" > /dev/stdout api_root_path="/app/suitecrm/Api" echo "[ tmc ] api root path set to " $api_root_path > /dev/stdout # update apache conf files: # Set web server root to /app/suitecrm echo "[ tmc ] updating web server root..." > /dev/stdout sed -ri 's#DocumentRoot .*#DocumentRoot /app/suitecrm#' /etc/apache2/sites-available/suitecrm.conf sed -ri 's#<Directory /app/suitecrm/public>#<Directory /app/suitecrm>#' /etc/apache2/conf-available/suitecrm.conf echo "[ tmc ] done." > /dev/stdout # set permissions for SuiteCRM 7 directory structure echo "[ tmc ] setting permissions for SuiteCRM 7 directories..." > /dev/stdout cd /app/suitecrm chown -R www-data:www-data . chmod -R 755 . chmod -R 775 cache custom modules themes data upload chmod 775 config_override.php 2>/dev/null echo "[ tmc ] done." > /dev/stdout fi # start cron echo "[ tmc ] starting cron " > /dev/stdout service cron start # Generate SSL keys for OAuth2 if private key does not exist if [ ! -f "$api_root_path/V8/OAuth2/private.key" ]; then echo "[ tmc ] no SSL keys found - generating SSL keys in " $api_root_path > /dev/stdout openssl genrsa -out "$api_root_path/V8/OAuth2/private.key" 2048 openssl rsa -in "$api_root_path/V8/OAuth2/private.key" \ -pubout -out "$api_root_path/V8/OAuth2/public.key" fi # Set ownership and permissions for SSL keys if [ -f "$api_root_path/V8/OAuth2/private.key" ]; then echo "[ tmc ] setting ownership and permissions for SSL keys in " $api_root_path > /dev/stdout chown www-data:www-data "$api_root_path/V8/OAuth2"/*.key chmod 600 "$api_root_path/V8/OAuth2"/*.key fi # end instructions that are run only for main docker execution fi echo "[ tmc ] init complete. starting apache" > /dev/stdout exec "$@"
Other configuration files
You can adjust the values in the php.ini
file according to your host server and your resource requirements for SuiteCRM.
php.ini
date.timezone = Europe/Paris memory_limit = 1G upload_max_filesize = 512M post_max_size = 512M max_execution_time = 600 display_errors = off log_errors = on fastcgi_logging = off catch_workers_output = yes decorate_workers_output = no error_log = /proc/self/fd/1 log_level = notice
php.conf
; this file contains additional directives ; currently it does not seems necessary ; global directives go into php.ini --> conf.d/suitecrm.ini [global] error_log = /proc/self/fd/2 log_level = notice [www] catch_workers_output = yes decorate_workers_output = no pm.max_children = 50 pm.start_servers = 15 pm.min_spare_servers = 15 pm.max_spare_servers = 25 pm.max_requests = 500
The following files are used to configure the Apache server. We have to let Apache know that it is sitting behind a reverse proxy and that the base directory is /app/suitecrm/public
(modified if necessary by entrypoint.sh
, depending on the SuiteCRM version).
Of course, modify the server name to suit your needs.
apache.conf
<Directory /app/suitecrm/public> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory> LogLevel error ServerTokens Prod ServerSignature Off ServerName themoto.company RemoteIPHeader X-Forwarded-For RemoteIPInternalProxy 172.16.0.0/12
vhost.conf
<VirtualHost *:80> ServerName themoto.company ServerAlias * # this lets apache know that we're behind a https proxy SetEnvIf X-Forwarded-Proto "https" HTTPS=on DocumentRoot /app/suitecrm/public ErrorLog "/var/log/apache2/error.log" LogFormat "%h %l %u %t \"%r\" %>s %b" common CustomLog "/var/log/apache2/access.log" common </VirtualHost>
Finally, nothing special about the cron config file, but it is essential to run scheduled tasks in SuiteCRM:
cron-suitecrm
* * * * * www-data cd /var/www/suitecrm; php -f cron.php > /dev/null 2>&1
This is it, everything is ready. Now build the Docker with:
docker build --network=host -t mysuitecrm .
compose.yaml file
To launch the server and access SuiteCRM, you need to create a compose.yaml
file in another directory, with your stack: a database server, and the SuiteCRM docker.
The traefik parameters in the labels section enable the use of the reverse proxy. It should be adapted to suit your installation. Traefik automatically handles the retrieval of Let’s Encrypt certificates for the HTTPS protocol. It’s very efficient!
Anyway, don’t forget to point an A record in your DNS to your server for “your.suitecrm.url”.
services: suitecrm-db: image: mariadb container_name: suitecrm-db restart: always networks: - my-network environment: - MARIADB_ROOT_PASSWORD=your-root-password - MARIADB_USER=suite - MARIADB_DATABASE=suite - MARIADB_PASSWORD=suite-user-password volumes: - ./mariadb-data:/var/lib/mysql suitecrm-app: image: mysuitecrm container_name: suitecrm-app networks: - my-network restart: always environment: - SUITECRM_DATABASE_HOST=suitecrm-db - SUITECRM_DATABASE_PORT_NUMBER=3306 - SUITECRM_DATABASE_USER=suite - SUITECRM_DATABASE_NAME=suite - SUITECRM_DATABASE_PASSWORD=user-password - SUITECRM_HOST=your.suitecrm.url volumes: - ./suitecrm-data:/app/suitecrm depends_on: - suitecrm-db labels: - traefik.enable=true - traefik.http.routers.suitecrm.rule=Host(`your.suitecrm.url`) - traefik.http.routers.suitecrm.entrypoints=websecure # - traefik.http.routers.suitecrm.middlewares=server-whitelist@file networks: prometheus: name: my-network external: true
Voilà!
Next, you need to copy the SuiteCRM installation files into ./suitecrm-data
, and then all that’s left is to start the stack with:
docker compose up -d
(or docker-compose, with a hyphen, if you are using an older version of compose)
You now have an operational version of SuiteCRM.
Good job!