A Redmine upgrade replaces the Rails application code while keeping the production database, uploaded files, local configuration, plugins, and themes intact. The risky part is the handoff between the old application root and the new release root, because database migrations and plugin code can change state that a file copy alone cannot undo.
The release-archive path keeps the old Redmine tree available until the upgraded instance has passed a smoke test. Work from a maintenance shell or a staging copy first, and use the same application user, database adapter, plugin set, and attachment path that production uses.
The path placeholders use an active deployment symlink, an old release root, and a target release root. Replace them with the release pair being upgraded, and do not point production traffic at the new root until backups, migrations, and login checks have passed.
Related: How to back up and restore Redmine
Related: How to install a Redmine plugin
Related: How to install a Redmine theme
$ readlink --canonicalize /opt/redmine-current /opt/redmine-6.0.10
If the deployment does not use a symlink, use the root configured in the Puma, Passenger, systemd, or container service that runs Redmine.
$ cd /opt/redmine-6.0.10
$ RAILS_ENV=production bundle exec rails runner 'puts Redmine::VERSION.to_s' 6.0.10.stable
$ ruby -e 'puts RUBY_VERSION' 3.4.9
Redmine 6.1 supports Ruby 3.2, 3.3, and 3.4. Check the official requirements table for the target release, including the supported PostgreSQL, MySQL, SQL Server, or SQLite versions.
Stop the application server, disable the load balancer target, or put the site behind a maintenance page. Keep the database service running so backup and migration commands can connect.
$ sudo install --directory --mode=0750 /var/backups/redmine/upgrade-6.1.3
$ pg_dump --username=redmine --host=localhost --format=custom --file=/var/backups/redmine/upgrade-6.1.3/redmine.dump redmine
Use the database name, host, and user from /opt/redmine-6.0.10/config/database.yml. pg_dump prompts for the password unless the database account uses peer authentication or a protected .pgpass file.
Related: How to back up and restore Redmine
$ sudo tar --create --gzip --file=/var/backups/redmine/upgrade-6.1.3/redmine-files-config.tar.gz --directory=/opt/redmine-6.0.10 files config plugins themes
The archive can contain database passwords, SMTP credentials, plugin configuration, and uploaded files. Store it where only Redmine administrators and backup operators can read it.
$ curl --location --remote-name https://www.redmine.org/releases/redmine-6.1.3.tar.gz
Use the exact stable release selected for the maintenance window.
$ sudo tar --extract --gzip --file=redmine-6.1.3.tar.gz --directory=/opt
$ sudo cp --archive /opt/redmine-6.0.10/config/database.yml /opt/redmine-6.1.3/config/database.yml
$ sudo cp --archive /opt/redmine-6.0.10/config/configuration.yml /opt/redmine-6.1.3/config/configuration.yml
Skip copying configuration.yml only when the deployment intentionally supplies those settings through another controlled mechanism. Do not overwrite /opt/redmine-6.1.3/config/settings.yml with the old file.
$ sudo rsync --archive /opt/redmine-6.0.10/files/ /opt/redmine-6.1.3/files/
$ sudo rsync --archive /opt/redmine-6.0.10/plugins/ /opt/redmine-6.1.3/plugins/
Only keep plugins that list compatibility with the target Redmine release. Remove or update incompatible plugins before running plugin migrations.
$ sudo rsync --archive /opt/redmine-6.0.10/themes/ /opt/redmine-6.1.3/themes/
Redmine 6.x uses /themes. For older source roots, review any custom themes under /public/themes and install a target-compatible theme in the current theme path.
Related: How to install a Redmine theme
$ sudo chown --recursive redmine:redmine /opt/redmine-6.1.3/files /opt/redmine-6.1.3/log /opt/redmine-6.1.3/tmp /opt/redmine-6.1.3/public/assets
Use the Unix user and group that run the Redmine application server.
$ sudo find /opt/redmine-6.1.3/files /opt/redmine-6.1.3/log /opt/redmine-6.1.3/tmp /opt/redmine-6.1.3/public/assets -type f -exec chmod -x {} +
$ cd /opt/redmine-6.1.3
$ bundle config set --local without 'development test'
$ bundle install Bundle complete! 53 Gemfile dependencies, 96 gems now installed. Gems in the groups 'development' and 'test' were not installed.
The exact dependency count changes by Redmine release, database adapter, and any Gemfile.local entries.
$ RAILS_ENV=production bundle exec rake db:migrate == 20250423065135 CreateReactions: migrating ================================== -- create_table(:reactions) -> 0.0006s == 20250423065135 CreateReactions: migrated (0.0010s) ========================= ##### snipped ##### == 20250611092227 EnablePkce: migrated (0.0015s) ==============================
Database migrations change production tables. Stop here and restore from the database backup if migrations fail in a way that cannot be corrected during the maintenance window.
$ RAILS_ENV=production bundle exec rake redmine:plugins:migrate
No output is normal when copied plugins have no pending migrations.
$ RAILS_ENV=production bundle exec rake tmp:cache:clear
$ sudo ln --symbolic --force --no-dereference /opt/redmine-6.1.3 /opt/redmine-current
If the application service points directly at a fixed directory, update that service or web-server root to the new release path instead of using a deployment symlink.
Use the restart path for the deployment, such as the systemd unit for Puma, the web server that runs Passenger, or the container service.
$ RAILS_ENV=production bundle exec rails runner 'puts Redmine::VERSION.to_s' 6.1.3.stable
$ curl --head https://redmine.example.net/login HTTP/2 200 content-type: text/html; charset=utf-8 x-frame-options: SAMEORIGIN ##### snipped #####
Use the production URL, staging URL, or load balancer health endpoint that normally fronts Redmine.
Redmine recommends checking permissions after an upgrade because new features can add role-level controls.
$ RAILS_ENV=production bundle exec rails runner 'puts Project.find_by!(identifier: "field-service").name' Field Service Portal
Replace field-service with a project identifier that should exist after the upgrade. A successful lookup confirms the upgraded application can read the migrated production database.