Install system dependencies
$ sudo apt update && sudo apt upgrade -y
$ sudo apt install -y \
apache2 libapache2-mod-passenger \
mariadb-server mariadb-client \
libmariadb-dev \
build-essential zlib1g-dev libssl-dev \
libreadline-dev libyaml-dev libffi-dev \
libxml2-dev libxslt1-dev \
imagemagick libmagickwand-dev \
git curl wget
Install Ruby 3.2 with rbenv
Clone rbenv
$ rm -rf ~/.rbenv
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
Clean previous entries and add to PATH
$ grep -v rbenv ~/.bashrc > /tmp/bashrc_tmp && mv /tmp/bashrc_tmp ~/.bashrc
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc
$ source ~/.bashrc
Install ruby-build
$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
Install Ruby 3.2.4 keeping sources (takes 10-20 minutes)
$ rbenv install --keep 3.2.4
$ rbenv global 3.2.4
Verify
$ ruby -v
Install Bundler
$ gem install bundler
Recompile Ruby into /usr/local for Apache/Passenger
This is required because Apache runs as www-data and has no access to /root/.rbenv.
Recompile with accessible prefix
$ cd /root/.rbenv/sources/3.2.4/ruby-3.2.4
$ ./configure --prefix=/usr/local/ruby3.2.4 --enable-shared
$ make -j$(nproc)
$ sudo make install
Set correct permissions
$ sudo chmod -R a+rX /usr/local/ruby3.2.4
Register libraries in the system
$ echo "/usr/local/ruby3.2.4/lib" | sudo tee /etc/ld.so.conf.d/ruby3.2.conf
$ sudo ldconfig
Verify
$ /usr/local/ruby3.2.4/bin/ruby -v
Set up MariaDB
$ sudo systemctl start mariadb
$ mysql_secure_installation
Create the database and user for Redmine:
$ sudo mysql -u root -p
And execute the following SQL commands:
CREATE DATABASE redmine CHARACTER SET utf8mb4;
CREATE USER 'redmine'@'localhost' IDENTIFIED BY 'YourSecurePassword';
GRANT ALL PRIVILEGES ON redmine.* TO 'redmine'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Restore the Redmine 5.0.1 dump
$ unzip -c /path/to/backup/redmine_backup_mysql.sql.gz | mysql -u redmine -p redmine
⚠️ Important: You are importing the 5.0.1 data as-is. Redmine 6.1 will run the pending migrations in Step 8 and update the schema automatically. Do not touch the schema manually.
Clone Redmine 6.1 from Git
$ sudo mkdir -p /opt/redmine
$ git clone https://github.com/redmine/redmine.git /opt/redmine
$ cd /opt/redmine
Switch to the stable 6.1 branch
$ git checkout 6.1-stable
✅ For future updates within the 6.1 branch you will only need git pull.
Configure the database
$ cp /opt/redmine/config/database.yml.example /opt/redmine/config/database.yml
$ nano /opt/redmine/config/database.yml
Keep only the production block:
production:
adapter: mysql2
database: redmine
host: localhost
username: redmine
password: "YourSecurePassword"
encoding: utf8mb4
variables:
tx_isolation: "READ-COMMITTED"
Install gems and prepare Redmine
$ cd /opt/redmine
Install gems
$ bundle config set --local without 'development test'
$ bundle install
Generate secret token
$ bundle exec rake generate_secret_token
Run migrations (updates schema from 5.0.1 to 6.1)
$ RAILS_ENV=production bundle exec rake db:migrate
IMPORTANT: DO NOT run redmine:load_default_data since we already have 5.0.1 data
Compile assets
$ RAILS_ENV=production bundle exec rake assets:precompile
Set correct permissions
$ sudo chown -R www-data:www-data /opt/redmine
$ sudo chmod -R 755 /opt/redmine
$ sudo chmod -R 777 /opt/redmine/tmp /opt/redmine/log /opt/redmine/files /opt/redmine/public/plugin_assets
Configure Apache with Passenger
$ sudo nano /etc/apache2/sites-available/redmine.conf
Edit with this content:
apache<VirtualHost *:80>
ServerName your-domain-or-ip
DocumentRoot /opt/redmine/public
<Directory /opt/redmine/public>
Options -MultiViews
AllowOverride All
Require all granted
</Directory>
PassengerRuby /usr/local/ruby3.2.4/bin/ruby
PassengerAppEnv production
PassengerFriendlyErrorPages on
ErrorLog ${APACHE_LOG_DIR}/redmine_error.log
CustomLog ${APACHE_LOG_DIR}/redmine_access.log combined
</VirtualHost>
And exceute:
$ sudo a2ensite redmine.conf
$ sudo a2enmod passenger rewrite
$ sudo systemctl restart apache2
Verify it starts correctly
Check logs for any errors
$ sudo tail -f /var/log/apache2/redmine_error.log
$ sudo tail -f /opt/redmine/log/production.log
Configure OAuth for Outlook
Once Redmine is up and running, go to Administration → Settings → Email and you will be able to configure SMTP with OAuth2 for Microsoft 365/Outlook. When you get to that point, let me know and I will explain how to register the app in Azure AD and fill in all the required fields.
IMPORT DATA FROM REDMINE < v5.1
Empty the current database
$ sudo mysql -u root -p -e "DROP DATABASE redmine; CREATE DATABASE redmine CHARACTER SET utf8mb4;"
$ sudo mysql -u root -p -e "GRANT ALL PRIVILEGES ON redmine.* TO 'redmine'@'localhost'; FLUSH PRIVILEGES;"
Import the new dump
$ gunzip -c /path/to/backup/new_dump.sql.gz | mysql -u redmine -p redmine
Run the migrations
$ cd /opt/redmine
$ RAILS_ENV=production bundle exec rake db:migrate
Clear the cache
$ RAILS_ENV=production bundle exec rake tmp:cache:clear
Restart Apache
$ sudo systemctl restart apache2
⚠️ Do NOT run redmine:load_default_data because you have real production data.
ADD EMAIL NOTIFICATION
Add this configuration file /opt/redmine/configuration.yml:
production:
email_delivery:
delivery_method: :smtp
smtp_settings:
enable_starttls_auto: true
address: "smtp.office365.com"
port: 587
domain: "name.com"
authentication: :login
user_name: "support@name.com"
password: "password"
And restart apache2.
CONFIGURE HTTPS WITH INTERNAL CA
Create the internal CA
Creates the directory where all CA files will be stored and moves into that directory so all files are created there:
$ sudo mkdir -p /etc/ssl/myCA
$ cd /etc/ssl/myCA
Generates the CA's private key with 4096-bit encryption. This key must never be shared with anyone — it's what makes your CA trustworthy.
$ sudo openssl genrsa -out myCA.key 4096
Creates the CA's root certificate using the private key above. -x509 means it's self-signed, -days 3650 makes it valid for 10 years, and -subj fills in the company details automatically without an interactive prompt.
$ sudo openssl req -x509 -new -nodes \
-key myCA.key \
-sha256 -days 3650 \
-out myCA.crt \
-subj "/C=ES/ST=Cantabria/L=Santander/O=COMPANY/CN=COMPANY CA"
Generate the Redmine certificate
Generates a private key specifically for the Redmine server. 2048-bit is sufficient for a server certificate.
$ sudo openssl genrsa -out redmine.key 2048
Creates a Certificate Signing Request (CSR) — essentially a request saying "I need a certificate for redmine.comapany.net". It's not a certificate yet, just the request.
$ sudo openssl req -new \
-key redmine.key \
-out redmine.csr \
-subj "/C=ES/ST=Cantabria/L=Santander/O=COMPANY/CN=redmine.company.net"
Creates a configuration file with extra certificate settings. The most important part is subjectAltName — modern browsers require this field to trust a certificate. Without it Chrome and Firefox would reject it.
$ sudo tee redmine.ext << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage=digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = redmine.company.net
EOF
This is where the CA actually signs the Redmine certificate. It takes the CSR (redmine.csr), signs it with the CA key (myCA.key), applies the extra settings (redmine.ext), and produces the final certificate (redmine.crt) valid for 3 years.
sudo openssl x509 -req \
-in redmine.csr \
-CA myCA.crt \
-CAkey myCA.key \
-CAcreateserial \
-out redmine.crt \
-days 1095 \
-sha256 \
-extfile redmine.ext
Update Apache configuration with HTTPS
Enables the SSL module in Apache so it can handle HTTPS connections.
$ sudo a2enmod ssl
Replace the entire content with:
$ sudo nano /etc/apache2/sites-available/redmine.conf
# Redirect HTTP → HTTPS
<VirtualHost *:80>
ServerName redmine.company.net
Redirect permanent / https://redmine.company.net/
</VirtualHost>
# HTTPS
<VirtualHost *:443>
ServerName redmine.company.net
DocumentRoot /opt/redmine/public
SSLEngine on
SSLCertificateFile /etc/ssl/myCA/redmine.crt
SSLCertificateKeyFile /etc/ssl/myCA/redmine.key
<Directory /opt/redmine/public>
Options -MultiViews
AllowOverride All
Require all granted
</Directory>
PassengerRuby /usr/local/ruby3.2.4/bin/ruby
PassengerAppEnv production
PassengerFriendlyErrorPages on
ErrorLog ${APACHE_LOG_DIR}/redmine_error.log
CustomLog ${APACHE_LOG_DIR}/redmine_access.log combined
</VirtualHost>
Before Restarts Apache Fix /var/www permissions for Passenger: Allow www-data to write to /var/www
$ sudo mkdir -p /var/www
$ sudo chown www-data:www-data /var/www
Restarts Apache to apply all the changes.
$ sudo systemctl restart apache2
Distribute the root certificate to client machines
Installs the CA root certificate into the Windows trusted certificate store. Once done, any certificate signed by your CA will be trusted automatically in that machine.
> certutil -addstore -f "ROOT" "myCA.crt"
On Linux, copies the CA certificate to the system trust store and refreshes it so all applications trust your CA.
$ sudo cp myCA.crt /usr/local/share/ca-certificates/
$ sudo update-ca-certificates
Tells the machine where to find redmine.company.net since it's not a public domain registered on the internet. Without this entry the browser wouldn't know which server to connect to.
$ sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain myCA.crt
```
On macOS, adds the CA certificate to the system keychain so Safari, Chrome and other apps trust it.
---
### STEP 5 — Add DNS entry
```
SERVER_IP redmine.company.net