How to back up and restore Redmine

Redmine recovery depends on both the application database and the uploaded files stored beside the Rails application. A database dump without the files directory can bring back projects and issues but leave attachments missing, while a file copy without the database cannot rebuild project history.

A PostgreSQL-backed Redmine installation can be backed up with pg_dump and restored with pg_restore into a staging database. Uploaded files, local configuration, and plugins are archived separately from the database so each part can be inspected before a restore drill.

Run the restore against a disposable staging instance before relying on the backup for a maintenance window or disaster recovery plan. The staging Redmine version should match production, and its /config/database.yml file should point at the drill database before the smoke test reads recovered project data.

Steps to back up and restore Redmine with PostgreSQL:

  1. Identify the Redmine root, database settings, and attachment path.

    The database settings are usually in /opt/redmine/config/database.yml. Uploaded files default to /opt/redmine/files unless attachments_storage_path in /opt/redmine/config/configuration.yml points elsewhere.

  2. Create a dated backup directory.
    $ sudo mkdir --parents /var/backups/redmine/2026-06-26
  3. Dump the PostgreSQL database in custom format.
    $ pg_dump --username=redmine --host=localhost --format=custom --file=/var/backups/redmine/2026-06-26/redmine.dump redmine

    Use the database name, host, and user from /opt/redmine/config/database.yml. pg_dump prompts for the password unless the database account uses peer authentication or a protected .pgpass file.

  4. Archive uploaded files, local configuration, and plugins.
    $ sudo tar --create --gzip --file=/var/backups/redmine/2026-06-26/redmine-files-config.tar.gz --directory=/opt/redmine files config plugins

    The archive contains /opt/redmine/config/database.yml, which may include database credentials. Store the backup where only Redmine administrators and backup operators can read it.

  5. List the database dump contents.
    $ pg_restore --list /var/backups/redmine/2026-06-26/redmine.dump
    ;
    ; Archive created at 2026-06-26 12:20:00 UTC
    ;     dbname: redmine
    ;     TOC Entries: 482
    ;     Compression: gzip
    ;
    ##### snipped #####
    3357; 0 16768 TABLE DATA public projects redmine
    3358; 0 16777 TABLE DATA public projects_trackers redmine
    3359; 0 16782 TABLE DATA public roles redmine
    ##### snipped #####
  6. List the file archive contents.
    $ tar --list --gzip --file=/var/backups/redmine/2026-06-26/redmine-files-config.tar.gz
    files/
    files/20260626010101_field_service_status.txt
    config/
    config/configuration.yml
    config/database.yml
    plugins/
  7. Create an empty drill database on the staging database server.
    $ createdb --username=redmine --host=localhost --owner=redmine redmine_restore

    Use a database administrator account for this step when the redmine database user cannot create databases.

  8. Restore the dump into the drill database.
    $ pg_restore --username=redmine --host=localhost --dbname=redmine_restore --clean --if-exists /var/backups/redmine/2026-06-26/redmine.dump
  9. Extract the uploaded files into the staging Redmine root.
    $ sudo tar --extract --gzip --file=/var/backups/redmine/2026-06-26/redmine-files-config.tar.gz --directory=/opt/redmine files

    Extract configuration or plugin files separately only after checking that paths, database names, plugin versions, and secrets match the staging instance.

  10. Point the staging Redmine database configuration at the drill database.
    /opt/redmine/config/database.yml
    production:
      adapter: postgresql
      database: redmine_restore
      host: localhost
      username: redmine
      password: "<redmine database password>"
  11. Start the staging Redmine application against the drill database.

    Use the service, container, Passenger restart, or process manager command that runs the staging instance.

  12. Verify that Redmine can read a known restored project from the drill database.
    $ RAILS_ENV=production bundle exec rails runner 'puts Project.find_by!(identifier: "field-service").name'
    Field Service Portal
  13. Check the restored login page over HTTP.
    $ curl --head http://redmine-staging.example.net/login
    HTTP/1.1 200 OK
    Content-Type: text/html; charset=utf-8
    X-Frame-Options: SAMEORIGIN