Infiniroot Blog: We sometimes write, too.

Of course we cannot always share details about our work with customers, but nevertheless it is nice to show our technical achievements and share some solutions.

How to migrate OTRS 6.x (community edition) to another server

Published on February 4th 2021


A couple of years ago, we migrated our OTRS application (back then using OTRS 3.2) to another server. We documented this and wrote about the gotchas in an article OTRS migration to another server; what to look out for.

Now it's 2021 and our OTRS, meanwhile at version 6.0.x, needed to be migrated again. In this article we share our migration experience to a Debian 10 (Buster) server.

Preparation of the target server

There are a couple of things to be prepared on the new target server. The obvious gotchas are all the Perl packages - but also the database (we chose MariaDB 10.3) needs to be prepared.

Installation of Perl packages

To install all the required Perl packages (with a MySQL backend for OTRS):

root@otrs:~# apt-get install libdbi-perl libdbd-mysql-perl libgd-gd2-perl libgd-text-perl libgd-graph-perl libjson-xs-perl libmail-imapclient-perl libapache-dbi-perl libnet-dns-perl libnet-smtp-tls-butmaintained-perl libnet-ssleay-perl libpdf-api2-perl libtext-csv-perl libxml-parser-perl libyaml-perl libarchive-zip-perl libtemplate-perl libxml-libxml-perl libdatetime-perl libcrypt-eksblowfish-perl libmoo-perl

Installation of database

MariaDB was installed directly from the Debian repositories:

root@otrs:~# apt-get install mariadb-server
root@otrs:~# mysql_secure_installation

The database needs some fine tuning because prior to importing a (most likely) very large OTRS database dump, the max_allowed_package value needs to be increased. Otherwise the database import may fail with an error message "mysql has gone away". So we've added "max_allowed_packet=256M" in /etc/mysql/mariadb.conf.d/50-server.cnf, followed by a restart.

Now the otrs database and user can be created:

MariaDB [(none)]> CREATE DATABASE otrs;
MariaDB [(none)]> GRANT ALL ON otrs.* TO 'otrs'@'localhost' IDENTIFIED BY 'password';

If you are unsure which credentials are currently used, take a look at /opt/otrs/Kernel/Config.pm on the current/old server.

Installation of Apache

OTRS runs on an Apache web server with mod_perl. A few other modules are required as well.

Installation of Apache and mod_perl:

root@otrs:~# apt-get install apache2 libapache2-mod-perl2

Afterwards the needed modules are enabled:

root@otrs:~# a2enmod perl
root@otrs:~# a2enmod headers
root@otrs:~# a2enmod expires
root@otrs:~# systemctl restart apache2

Note: Apache MPM Prefork should be used.

Apache vhost configuration

Best practice is to create a symlink "otrs.conf" in /etc/apache2/conf-available, point this symlink to /opt/otrs/scripts/apache2-httpd.include.conf and enable this config with a2enconf otrs.conf.

However we're using the following Apache vhost config, which also works and fits better in our deployment procedure. You obviously need to adjust to your own environment and domain:

root@otrs:~# cat /etc/apache2/sites-available/otrs.example.com.conf
<VirtualHost *:80>
  ServerName otrs.example.com
  DocumentRoot /opt/otrs/var/httpd/htdocs
  ScriptAlias /otrs/ "/opt/otrs/bin/cgi-bin/"
  Alias /otrs-web/ "/opt/otrs/var/httpd/htdocs/"
  CustomLog /var/log/apache2/otrs.example.com.access.log combined
  ErrorLog /var/log/apache2/otrs.example.com.error.log

# activate this if you are using an Oracle database
#SetEnv ORACLE_HOME /path/to/your/oracle/
#SetEnv ORACLE_SID YOUR_SID
#SetEnv NLS_LANG AMERICAN_AMERICA.UTF8
#SetEnv NLS_DATE_FORMAT 'YYYY-MM-DD HH24:MI:SS'

<IfModule mod_perl.c>

    # Setup environment and preload modules
    Perlrequire /opt/otrs/scripts/apache2-perl-startup.pl

    # Reload Perl modules when changed on disk
    PerlModule Apache2::Reload
    PerlInitHandler Apache2::Reload

    # general mod_perl2 options
    <Location /otrs>
        ErrorDocument 403 /otrs/index.pl
        SetHandler  perl-script
        PerlResponseHandler ModPerl::Registry
        Options +ExecCGI
        PerlOptions +ParseHeaders
        PerlOptions +SetupEnv

        <IfModule mod_version.c>
            <IfVersion < 2.4>
                Order allow,deny
                Allow from all
            </IfVersion>
            <IfVersion >= 2.4>
                Require all granted
            </IfVersion>
        </IfModule>
        <IfModule !mod_version.c>
            Order allow,deny
            Allow from all
        </IfModule>
    </Location>

    # mod_perl2 options for GenericInterface
    <Location /otrs/nph-genericinterface.pl>
        PerlOptions -ParseHeaders
    </Location>

</IfModule>

<Directory "/opt/otrs/bin/cgi-bin/">
    AllowOverride None
    Options +ExecCGI -Includes

    <IfModule mod_version.c>
        <IfVersion < 2.4>
            Order allow,deny
            Allow from all
        </IfVersion>
        <IfVersion >= 2.4>
            Require all granted
        </IfVersion>
    </IfModule>
    <IfModule !mod_version.c>
        Order allow,deny
        Allow from all
    </IfModule>

    <IfModule mod_deflate.c>
        AddOutputFilterByType DEFLATE text/html text/javascript text/css text/xml application/json text/json
    </IfModule>
</Directory>

<Directory "/opt/otrs/var/httpd/htdocs/">
    AllowOverride None

    <IfModule mod_version.c>
        <IfVersion < 2.4>
            Order allow,deny
            Allow from all
        </IfVersion>
        <IfVersion >= 2.4>
            Require all granted
        </IfVersion>
    </IfModule>
    <IfModule !mod_version.c>
        Order allow,deny
        Allow from all
    </IfModule>

    <IfModule mod_deflate.c>
        AddOutputFilterByType DEFLATE text/html text/javascript text/css text/xml application/json text/json
    </IfModule>

    # Make sure CSS and JS files are read as UTF8 by the browsers.
    AddCharset UTF-8 .css
    AddCharset UTF-8 .js

    # Set explicit mime type for woff fonts since it is relatively new and apache may not know about it.
    AddType application/font-woff .woff

</Directory>

<IfModule mod_headers.c>
    # Cache css-cache for 30 days
    <Directory "/opt/otrs/var/httpd/htdocs/skins/*/*/css-cache">
        <FilesMatch "\.(css|CSS)$">
            Header set Cache-Control "max-age=2592000 must-revalidate"
        </FilesMatch>
    </Directory>

    # Cache css thirdparty for 4 hours, including icon fonts
    <Directory "/opt/otrs/var/httpd/htdocs/skins/*/*/css/thirdparty">
        <FilesMatch "\.(css|CSS|woff|svg)$">
            Header set Cache-Control "max-age=14400 must-revalidate"
        </FilesMatch>
    </Directory>

    # Cache js-cache for 30 days
    <Directory "/opt/otrs/var/httpd/htdocs/js/js-cache">
        <FilesMatch "\.(js|JS)$">
            Header set Cache-Control "max-age=2592000 must-revalidate"
        </FilesMatch>
    </Directory>

    # Cache js thirdparty for 4 hours
    <Directory "/opt/otrs/var/httpd/htdocs/js/thirdparty/">
        <FilesMatch "\.(js|JS)$">
            Header set Cache-Control "max-age=14400 must-revalidate"
        </FilesMatch>
    </Directory>
</IfModule>

</VirtualHost>

You cannot enable this config yet, or Apache will fail (due to the missing Perlrequire).

Prepare OTRS user

Obviously the otrs user needs to be created on the system. Important is to not forget to add the otrs user to the www-data group:

root@otrs:~# useradd -r -d /opt/otrs/ -c 'OTRS user' -s /bin/bash otrs
root@otrs:~# usermod -G www-data -a otrs

Prepare OTRS crontab

The crontab for OTRS has been significantly reduced over the years because everything is now handled within the OTRS Daemon. However the crontab still contains one job: To make sure the Daemon is started:

root@otrs:~# crontab -l -u otrs
# Who gets the cron emails?
MAILTO="root@localhost"

# check OTRS daemon status
#*/5 * * * *    $HOME/bin/otrs.Daemon.pl start >> /dev/null

In this prepared crontab the job is disabled. It needs to be enabled once the data was migrated.

Stop OTRS on old server

Now it's time to migrate OTRS but first OTRS needs to be stopped on the old server to get a consistent data state. As OTRS user, stop the Daemon:

root@old:~# su - otrs
$ ./bin/otrs.Daemon.pl stop

To prevent that the daemon is automatically started again, don't forget to disable the cron jobs!

Prepare the data

Now that OTRS is stopped and no new tickets are coming in (due to the stopped daemon), the data can be prepared. The full /opt/otrs directory can be packaged for an easy transfer:

root@old:~# tar -czf /tmp/otrs.tar.gz /opt/otrs

And of course the database needs to be dumped, too:

root@old:~# mysqldump --single-transaction --quick otrs | gzip > /tmp/otrs.sql.gz

Make sure the /tmp directory has enough available space or chose another location where to save the dump.

Transfer the data

Files (otrs.tar.gz) and database (otrs.sql.gz) need to be transferred to the new target server. That's up to you how you do this. We're using scp for this.

Restore the data on the new server

Once the data was transferred to the new server, it can be unpacked and restored.

Database:

root@otrs:/tmp# gunzip < otrs.sql.gz | mysql otrs

For the files:

root@otrs:/tmp# tar -xzf otrs.tar.gz -C /

Note: You might have to adjust the destination path or extract without -C / and then move the otrs directory manually to /opt.

Fix permissions

Chances are high that the otrs user on the old server used a different UID/GID than on the new server. Therefore the unpacked /opt/otrs needs a reset of file permissions:

root@otrs:~# /opt/otrs/bin/otrs.SetPermissions.pl --otrs-user=otrs --web-group=www-data /opt/otrs

Enable Apache config

Now that /opt/otrs exists on the new server, the Apache vhost config for OTRS can be enabled:

root@otrs:~# a2ensite otrs.example.com.conf
root@otrs:~# systemctl reload apache2

And you should be able to visit the OTRS site now on the new server.

Note: Don't forget to change your DNS records for your OTRS domain.

Start OTRS daemon

The only missing piece to completion is to start the OTRS daemon processes:

root@otrs:~# su - otrs
otrs@otrs:~$ ./bin/otrs.Daemon.pl start

And voilĂ , here they are:

otrs@otrs:~$ ps auxf | grep otrs
root     16361  0.0  0.0   9944  3932 ?        S    15:49   0:00  \_ su - otrs
otrs     16362  0.0  0.0   7100  3744 ?        S    15:49   0:00      \_ -bash
otrs     16524  0.0  0.0  10784  3128 ?        R+   15:50   0:00          \_ ps auxf
otrs     16529  0.0  0.0   6076   884 ?        S+   15:50   0:00          \_ grep otrs
otrs      5150  0.0  0.0  21028  7020 ?        Ss   08:11   0:00 /lib/systemd/systemd --user
otrs      5151  0.0  0.0 170592  2236 ?        S    08:11   0:00  \_ (sd-pam)
otrs      5235  0.0  0.2  59728 48504 ?        S    08:11   0:21 /usr/bin/perl -X ./bin/otrs.Daemon.pl start
otrs     12529  0.0  0.3  82036 65640 ?        S    15:11   0:00  \_ /usr/bin/perl -X ./bin/otrs.Daemon.pl start
otrs     12530  0.0  0.4  85616 68828 ?        S    15:11   0:01  \_ /usr/bin/perl -X ./bin/otrs.Daemon.pl start
otrs     12576  0.0  0.3  76240 59796 ?        S    15:12   0:02  \_ /usr/bin/perl -X ./bin/otrs.Daemon.pl start
otrs     12579  0.0  0.3  74956 58148 ?        S    15:12   0:03  \_ /usr/bin/perl -X ./bin/otrs.Daemon.pl start
otrs     12637  0.0  0.3  75100 58440 ?        S    15:12   0:04  \_ /usr/bin/perl -X ./bin/otrs.Daemon.pl start

Now enable the task in the crontab:

otrs@otrs:~$ crontab -l
# Who gets the cron emails?
MAILTO="root@localhost"

# check OTRS daemon status
*/5 * * * *    $HOME/bin/otrs.Daemon.pl start >> /dev/null

Potential problems / troubleshooting

You should follow the logs in /opt/otrs/var/log/Daemon/ for errors.