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 of our implemented solutions.
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.
Update 2023: If you are looking for a way to migrate OTRS to Znuny, check out the ((OTRS)) Community Edition to Znuny migration article on the Infiniroot Blog.
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.
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
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.
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.
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).
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
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.
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!
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.
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.
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.
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
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.
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
You should follow the logs in /opt/otrs/var/log/Daemon/ for errors.