
Redmineとは、Ruby on Railsで書かれ、GNU GPL v2で公開されているオープンソースのプロジェクト管理Webアプリケーションです。作業を「チケット」という単位で登録し、種類(トラッカー)、状態(ステータス)、担当者で追跡します。月額で借りるSaaSではなく自分のサーバーに入れて動かすソフトウェアなので、インストール後の初期設定とバックアップは使う側の仕事になります。
対象は、社内の依頼や障害をExcelとメールで追っている情シスと、自宅ラボに課題管理を置きたい人です。執筆時点の最新安定版 Redmine 7.0.2 と PostgreSQL 17.11 を公式Dockerイメージで起動し、初回ログインで止まる箇所(組み込みの admin、空のトラッカー一覧、言語)を再現したうえで、初期設定をコマンドだけでやり直せる形にします。最後に pg_dump と添付ファイルのバックアップを別のスタックへ復元し、チケットと添付が戻ったことを件数とハッシュで照合します。どのツールにするかを決める段階なら、先にプロジェクト管理ツールの選び方で比較軸を固めてください。
この記事でわかること
- イメージのタグ、127.0.0.1 限定のポート、
SECRET_KEY_BASE、名前付きボリューム、DBのヘルスチェックを固定したcompose.yamlと、各行をそう書いた理由 - 初回ログインで止まる3か所(
adminの強制パスワード変更、空のトラッカーとステータス、英語のままの画面)の再現と、その確かめ方 - デフォルトデータの投入、管理者の作成、プロジェクトとチケットの登録を rake と REST API で再実行できる手順
- 2コンテナ合計で約243MiBだったメモリと、11MBだったDBサイズの測り方
- pg_dump と添付ファイルを新しいスタックに復元して件数と SHA-256 で照合する手順と、順番を誤ったときに起きる欠落
検証した構成と前提
検証は Docker Engine 29.1.3 と Docker Compose 2.40.3 が入ったLinuxサーバーで行いました。使うのは Docker Hub の公式イメージ redmine:7.0.2 と postgres:17.11 の2つで、ホスト側には curl、jq、openssl が要ります。サービス、ボリューム、up と down といったComposeの基本はDocker Compose入門に書いたので、ここでは Redmine に固有の部分に絞ります。
docker --version
docker compose versionDocker version 29.1.3, build 29.1.3-0ubuntu4.1
Docker Compose version 2.40.3+ds1-0ubuntu1Redmine公式サイトのダウンロードページ(執筆時点)では、7.0.x が新機能・バグ修正・セキュリティ修正を受ける最新の安定版、6.1.x はバグ修正とセキュリティ修正のみ、6.0.x は重要なセキュリティ修正のみ、5.1.x 以前はサポート対象外とされています。新しく建てるなら 7.0.x を選び、タグは 7.0.2 のようにパッチ版まで固定します。
データベースの版には判断が要ります。公式のインストール手順にある対応表は、Redmine 7.0 の推奨データベースとして PostgreSQL 14 を挙げています。一方、PostgreSQL公式サイトのサポート表では 14 の最終リリースは2026年11月12日、17 は2029年11月8日です。新規構築でサポート終了が目前の版を選ぶのは避けたいので、この記事は 17 系の 17.11 で検証しました。初回起動のマイグレーション、初期設定、REST API、バックアップと復元まで、この記事に載せた手順は 17.11 で最後まで通りましたが、公式の対応表に載っている組み合わせではありません。対応表どおりにしたい場合はイメージのタグを 14 系に変えてください(その構成はこの記事では試していません)。
コマンドに出てくるプロジェクト名 redmine-docker-compose-setup、ポート 18080、管理者のログインID ryoma(表示名 Ryoma T、メールアドレス [email protected])、プロジェクト識別子 it-helpdesk は検証用の例なので、自分の環境の値に読み替えてください。プロジェクト名を変えると、出力に出るコンテナ名とボリューム名の先頭も変わります。応答までの秒数、メモリ、ダンプのバイト数、添付の保存ファイル名(先頭が日時)は、実行のたびに違う値になります。
compose.yamlを1行ずつ決める
mkdir -p ~/redmine && cd ~/redmineこのディレクトリに、次の内容の compose.yaml をエディタで作ります。行末の番号は、直後の説明の番号に対応しています。
services:
db:
image: postgres:17.11 # [1]
restart: unless-stopped # [2]
environment:
POSTGRES_DB: redmine
POSTGRES_USER: redmine
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?} # [3]
volumes:
- db-data:/var/lib/postgresql/data # [4]
healthcheck: # [5]
test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U redmine -d redmine"]
interval: 5s
timeout: 3s
retries: 12
mem_limit: 512m # [6]
redmine:
image: redmine:7.0.2 # [1]
restart: unless-stopped # [2]
depends_on:
db:
condition: service_healthy # [5]
ports:
- "127.0.0.1:${REDMINE_PORT:?}:3000" # [7]
environment:
REDMINE_DB_POSTGRES: db # [8]
REDMINE_DB_DATABASE: redmine
REDMINE_DB_USERNAME: redmine
REDMINE_DB_PASSWORD: ${POSTGRES_PASSWORD:?} # [3]
SECRET_KEY_BASE: ${SECRET_KEY_BASE:?} # [9]
volumes:
- redmine-files:/usr/src/redmine/files # [4]
mem_limit: 1g # [6]
volumes:
db-data:
redmine-files:- イメージはパッチ版まで固定します。Docker Hub のタグ一覧では、執筆時点で
latestと7が7.0.2と同じダイジェストを指していました。動くタグを書くと、次に pull したときに版が上がり、起動時のマイグレーションが予定外に走ります。 restart: unless-stoppedは、止めろと指示するまで Docker がコンテナを再起動し続ける指定です。後半でDBのパスワードをわざと食い違わせたときも Redmine のコンテナは起動を繰り返し、原因を直すと人手で再起動しなくても応答が戻りました。ホストを再起動した後の自動起動は、この記事では試していません。- パスワードは
compose.yamlに書かず、.envのPOSTGRES_PASSWORDを2つのサービスが参照します。${変数名:?}は未設定なら起動させない書き方で、docker compose --env-file /dev/null config --quietとして.envを読ませずに試すとrequired variable POSTGRES_PASSWORD is missing a valueで止まりました。 - データは名前付きボリューム2つに置きます。
db-dataが PostgreSQL のデータ、redmine-filesが添付ファイルの保存先/usr/src/redmine/filesです。チケットの本文はDB、添付の実体はファイルと分かれているので、バックアップも2系統になります。PostgreSQL の公式イメージは 17 以下でマウント先が/var/lib/postgresql/data、18 以降は別の場所に変わっています。タグだけを 18 に書き換えると、ここで指定したマウント先とイメージの想定がずれます(18 への移行は試していません)。 healthcheckとcondition: service_healthyで、DBが接続を受け付けてから Redmine を起動します。pg_isreadyに-h 127.0.0.1を付けたのは、初回起動のログで PostgreSQL がまず Unix ソケットだけで立ち上がり、その後 TCP で待ち受け直していたからです(次の節で出力を示します)。アプリ側は TCP で接続するので、ヘルスチェックも TCP で見ます。mem_limitはコンテナごとのメモリ上限です。後述の実測は Redmine が約203MiB、PostgreSQL が約40MiBで、上限の 1GiB と 512MiB には届いていません。上限に達したときの挙動は試していません。ほかのサービスと同居するサーバーで、Redmine が周りのメモリを食いつぶさないための保険として入れています。- ポートは
127.0.0.1に限定します。"18080:3000"のようにIPを省くと、Composeの仕様ではすべてのネットワークインターフェースで待ち受けます。このコンテナが話すのは平文のHTTPなので、ほかのPCから使うときは手前に TLS を終端するリバースプロキシを置く前提です。ホスト側の番号は.envのREDMINE_PORTで決めます。 REDMINE_DB_POSTGRESにDBのサービス名を渡すと、イメージが接続設定を組み立てます。REDMINE_DB_*を何も渡さない場合は SQLite で動く仕様ですが、イメージの説明自体が複数人での本番利用向きではないとしているので使いません。SECRET_KEY_BASEはセッションCookieの署名に使う値です。渡さなければ起動時にコンテナの中で生成される仕様で、値が変わると既存のログインセッションは無効になります。コンテナを作り直しても同じ値が渡るよう.envに固定します。旧名のREDMINE_SECRET_KEY_BASEはイメージの説明で非推奨とされています。
.env は次のコマンドで作ります。先に install で権限 600 の空ファイルを置いてから書き込むので、umask の設定に関係なく、所有者と root 以外は読めません。パスワードとシークレットは openssl に生成させ、画面には変数名だけを出して確かめます。
install -m 600 /dev/null .env
cat > .env <<EOF
COMPOSE_PROJECT_NAME=redmine-docker-compose-setup
REDMINE_PORT=18080
POSTGRES_PASSWORD=$(openssl rand -hex 16)
SECRET_KEY_BASE=$(openssl rand -hex 64)
EOF
stat -c '%a %n' .env
cut -d= -f1 .env600 .env
COMPOSE_PROJECT_NAME
REDMINE_PORT
POSTGRES_PASSWORD
SECRET_KEY_BASECOMPOSE_PROJECT_NAME はコンテナ名とボリューム名の先頭に付く名前です。ディレクトリ名に頼らず .env に書いておくと、後半で同じ compose.yaml を別のディレクトリに置いて復元先を作るときに、名前とポートだけを変えれば済みます。
この .env 作成コマンドは、実行するたびに新しいパスワードを生成します。DBのボリュームができた後にやり直すと、PostgreSQL に保存済みのパスワードと食い違って Redmine が起動できなくなります(「つまずきポイント」で再現します)。作り直すのは、ボリュームごと消してやり直すときだけにしてください。
Redmineを起動してバージョンを確かめる
docker compose pull
docker compose up -d Container redmine-docker-compose-setup-db-1 Healthy
Container redmine-docker-compose-setup-redmine-1 Starting
Container redmine-docker-compose-setup-redmine-1 StartedDBが Healthy になってから Redmine のコンテナが始動しています。up -d が返った時点では、まだ Redmine は応答しません。次のループで待ち、状態と初回マイグレーションの数を見ます。
SECONDS=0; until curl -fs -o /dev/null http://127.0.0.1:18080/; do sleep 2; done; echo "応答まで ${SECONDS} 秒"
docker compose ps --format '{{.Service}}\t{{.State}}\t{{.Health}}\t{{.Ports}}'
docker compose logs redmine | grep ': migrated' | wc -l応答まで 12 秒
db running healthy 5432/tcp
redmine running 127.0.0.1:18080->3000/tcp
327up -d が返ってから応答まで12秒でした。この間にイメージのエントリーポイントが rake db:migrate を実行していて、ログの migrated を数えると 327 ありました。空のDBに対して走った処理で、後半の復元先スタックでは 0 でした。
docker compose exec -u redmine redmine bin/about
docker compose exec db psql -U redmine -d redmine -Atc 'select version()'Environment:
Redmine version 7.0.2.stable
Ruby version 4.0.7-p0 (2026-09-15) [x86_64-linux]
Rails version 8.1.4
Environment production
Database adapter PostgreSQL
PostgreSQL 17.11 (Debian 17.11-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bitbin/about はアプリ自身が返す版情報で、Redmine 7.0.2.stable、Ruby 4.0.7、Rails 8.1.4、DBアダプタは PostgreSQL でした。出力の先頭には no username found や cvs: not found といった行も出ます。版情報の SCM 欄を埋めるためにリポジトリ連携用のコマンドを呼んだときのメッセージと読め、以降の手順に影響はありませんでした。-u redmine を付けるのは、アプリ本体の puma が redmine ユーザーで動いているためです(docker compose exec redmine ps -o user,pid,args -p 1 の USER 欄で確認できます)。
docker compose logs db | grep -o 'listening on .*'
docker compose port redmine 3000
docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' $(docker compose ps -q redmine) | grep -c -E '^(REDMINE_DB_PASSWORD|SECRET_KEY_BASE)='listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
listening on IPv4 address "0.0.0.0", port 5432
listening on IPv6 address "::", port 5432
listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
127.0.0.1:18080
2最初の4行がヘルスチェックを TCP にした根拠です。PostgreSQL は初期化のあいだ Unix ソケットだけで待ち受け、その後に 0.0.0.0 の 5432 番で立ち上がり直しています。5行目は公開ポートで、docker compose port が返した待ち受け先は 127.0.0.1:18080 です。
最後の 2 は、コンテナの環境変数に REDMINE_DB_PASSWORD と SECRET_KEY_BASE が入っている数です。.env の権限 600 が防ぐのは、同じサーバーの別の一般ユーザーにファイルを読まれることまでです。docker inspect を実行できるユーザー(root と docker グループ)には値が見えるので、docker グループに入れる人を絞ることが実質的な防御になります。
初回ログインで止まる3か所を再現する
起動直後のRedmineは、そのままでは使い始められません。何が足りないのかをアプリ内部の値で先に見ておきます。~/redmine に次の check-state.rb を作ります。
admin = User.find_by_login("admin")
puts "trackers=#{Tracker.count} statuses=#{IssueStatus.count} priorities=#{IssuePriority.count} roles=#{Role.givable.count}"
puts "admin: locked=#{admin.locked?} must_change_passwd=#{admin.must_change_passwd}"
puts "rest_api_enabled=#{Setting.rest_api_enabled} login_required=#{Setting.login_required}"
puts "default_language=#{Setting.default_language} default_users_time_zone=#{Setting.default_users_time_zone.inspect}"
puts "host_name=#{Setting.host_name}"docker compose exec -T -u redmine redmine bin/rails runner - < check-state.rb
curl -s http://127.0.0.1:18080/trackers.json; echo
curl -s http://127.0.0.1:18080/issue_statuses.json; echo
curl -s -o /dev/null -w '%{http_code}\n' -u admin:admin http://127.0.0.1:18080/users/current.json
curl -s -o /dev/null -w '%{http_code}\n' -u admin:wrong-password http://127.0.0.1:18080/users/current.json
curl -s http://127.0.0.1:18080/login | grep -o '<html lang="[^"]*"'trackers=0 statuses=0 priorities=0 roles=0
admin: locked=false must_change_passwd=true
rest_api_enabled=0 login_required=0
default_language=en default_users_time_zone=""
host_name=localhost:3000
{"trackers":[]}
{"issue_statuses":[]}
403
403
<html lang="en"bin/rails runner - は標準入力のRubyコードをアプリの中で実行します。ファイルをリダイレクトで渡すため、docker compose exec には端末を割り当てない -T を付けます。出力から読み取れる引っかかりは次の3つです。
- 組み込みの admin:初期アカウントはログインID
admin、パスワードadminです。must_change_passwd=trueなので、ブラウザ相当のセッションでログインすると/projectsを開こうとしても/my/password(パスワード変更)へ転送されました。REST API のほうは、rest_api_enabled=0のこの時点ではadmin:adminでも、わざと間違えたパスワードでも同じ 403 でした(出力に2つ並んだ 403)。この 403 からは、パスワード変更の強制が効いているかどうかは読み取れません。 - 空のトラッカーとステータス:トラッカー、ステータス、優先度、メンバーに割り当てるロールがすべて 0 です。チケットの種類も状態も選べないままです。この状態で管理者が「管理」を開くと、「ロール、トラッカー、チケットのステータス、ワークフローがまだ設定されていません。」という案内と、言語の選択欄、「デフォルト設定をロード」のボタンが出ます(後で作る管理者のセッションで確認しました)。
- 言語:
default_language=enです。curl のように Accept-Language を送らないクライアントには、ログイン画面が<html lang="en">で返りました。加えて、デフォルトデータのトラッカー名やステータス名は、投入したときの言語でDBに保存されます。投入後に別の言語でやり直しても名前は変わらなかったので(「つまずきポイント」参照)、最初に言語を決めます。
初期設定をコマンドで済ませる
同じことは管理画面でもできますが、コマンドにしておくと、作り直しや検証環境でそのまま再実行できます。順番は、全体の設定、自分用の管理者、デフォルトデータです。
設定を4つ書き込む
~/redmine に init-settings.rb を作り、check-state.rb と同じ方法で流します。
Setting.default_language = "ja" # ブラウザの言語を判定できないときに使う言語
Setting.default_users_time_zone = "Tokyo" # 新しく作るユーザーのタイムゾーン
Setting.login_required = "1" # 未ログインならプロジェクトは /login へ転送、API は 401
Setting.rest_api_enabled = "1" # REST API を有効にする
puts "settings saved"docker compose exec -T -u redmine redmine bin/rails runner - < init-settings.rb
curl -s -o /dev/null -w '%{http_code}\n' -u admin:admin http://127.0.0.1:18080/users/current.json
curl -s -o /dev/null -w '%{http_code}\n' -u admin:wrong-password http://127.0.0.1:18080/users/current.jsonsettings saved
403
401画面では「管理」→「設定」にある項目です。7.0.2 の日本語表示では「RESTによるWebサービスを有効にする」が「連携」タブ、「認証が必要」が「認証」タブ、「デフォルトの言語」が「表示」タブにあります(タブ名は版によって変わります)。REST API を有効にした後は、admin:admin が 403 のまま、間違ったパスワードは 401 に変わりました(出力の2行目と3行目)。正しい資格情報のほうが 403 になるこの差は、must_change_passwd が true であることの影響と読めます。
login_required:公式の設定リファレンスは、有効にすると未ログインのユーザーはアプリケーションのどのページにもアクセスできなくなる、と説明しています(初期値は無効)。7.0.2 で未ログインのまま試した結果は「動作確認」の出力のとおりで、プロジェクトのページは/loginへ転送、API は 401 になりましたが、ログイン画面、アカウント登録(/account/register)、パスワード再発行(/account/lost_password)は 200 のままでした。アカウント登録の扱いは別の設定で決まり、公式リファレンスによると初期値は manual account activation(新しいユーザーのアカウントは作られるが、管理者の承認が必要)です。init-settings.rbはこの設定を変えていません。変える場所は「管理」→「設定」→「認証」タブの「ユーザーによるアカウント登録」です(変更は試していません)。社内用ならlogin_requiredは最初に有効にします。/trackers.jsonは、有効にする前は未ログインでも中身が返っていましたが(前の節の出力)、有効にした後は 401 です。default_users_time_zone:コンテナの時計は UTC です。タイムゾーンが未設定のユーザーとして同じチケットの更新日時を表示させると 07:32、Tokyo を設定したユーザーでは 16:32 でした。既定値を先に入れておいたので、この後に作る管理者には Tokyo が設定されます。default_language:ブラウザの言語を判定できないときに使われる言語です。設定後は、Accept-Language なしの curl にもログイン画面が日本語で返りました(後の出力で確認します)。
自分用の管理者を作り、組み込みのadminをロックする
次の init-admin.rb も ~/redmine に作ります。初期パスワードは openssl で生成して権限 600 のファイルに置き、環境変数経由でこのスクリプトに渡します。コマンドラインに書くのは $(cat admin-password.txt) という文字列なので、シェルの履歴にパスワードそのものは入りません。
password = ENV.fetch("NEW_ADMIN_PASSWORD")
user = User.new(firstname: "Ryoma", lastname: "T", mail: "[email protected]", language: "ja")
user.login = "ryoma"
user.admin = true
user.password = password
user.password_confirmation = password
user.save!
builtin = User.find_by_login("admin")
builtin.lock!
puts "created: login=#{user.login} admin=#{user.admin?} language=#{user.language} time_zone=#{user.pref.time_zone}"
puts "builtin admin: locked=#{builtin.reload.locked?}"install -m 600 /dev/null admin-password.txt && openssl rand -hex 12 > admin-password.txt
NEW_ADMIN_PASSWORD="$(cat admin-password.txt)" docker compose exec -T -u redmine -e NEW_ADMIN_PASSWORD redmine bin/rails runner - < init-admin.rbcreated: login=ryoma admin=true language=ja time_zone=Tokyo
builtin admin: locked=true実際の出力には、この2行の前後に ActiveJob の INFO 行が 6 つ混ざります。管理者の追加を知らせるセキュリティ通知メールのジョブですが、メール送信は未設定なので送られません(最後の節で触れます)。admin はどのRedmineにもあるIDで、初期パスワードも公開情報です。パスワードを変えて共有するより、個人名の管理者を作って admin はロックするほうが、誰の操作かをチケットの履歴で追えます。ロック後に管理者のパスワードをなくした場合は、同じ bin/rails runner でユーザーを作り直すことになります。
デフォルトデータを日本語で投入する
docker compose exec -u redmine -e REDMINE_LANG=ja redmine bundle exec rake redmine:load_default_data
docker compose exec -T -u redmine redmine bin/rails runner - < check-state.rb
curl -s -o /dev/null -w '%{http_code}\n' -u admin:admin http://127.0.0.1:18080/users/current.json
curl -s http://127.0.0.1:18080/login | grep -o '<html lang="[^"]*"'Default configuration data loaded.
trackers=3 statuses=6 priorities=5 roles=3
admin: locked=true must_change_passwd=true
rest_api_enabled=1 login_required=1
default_language=ja default_users_time_zone="Tokyo"
host_name=localhost:3000
401
<html lang="ja"redmine:load_default_data は公式のインストール手順にある rake タスクで、REDMINE_LANG で言語を渡すと対話なしで終わります。トラッカー 3、ステータス 6、優先度 5、ロール 3 が入り、ロックした admin は 401、ログイン画面は <html lang="ja"> になりました。管理者でログインしたセッションで「管理」を開き直すと、「デフォルト設定をロード」の案内は消えていました。
REST APIでプロジェクトと最初のチケットを作る
RedmineのREST APIは JSON と XML に対応し、認証はログインIDとパスワードのBasic認証か、ユーザーごとのAPIキーを X-Redmine-API-Key ヘッダーで送る方法が使えます。最初の1回だけパスワードでAPIキーを取り出し、以降はキーで呼びます。
install -m 600 /dev/null api-key.txt && curl -s -u "ryoma:$(cat admin-password.txt)" http://127.0.0.1:18080/users/current.json | jq -r .user.api_key > api-key.txt
curl -s -H "X-Redmine-API-Key: $(cat api-key.txt)" http://127.0.0.1:18080/users/current.json | jq -c '.user | {id, login, admin, firstname, lastname, mail}'{"id":5,"login":"ryoma","admin":true,"firstname":"Ryoma","lastname":"T","mail":"[email protected]"}APIキーはそのユーザーのIDとパスワードの代わりになる値です。api-key.txt も権限 600 で作り、画面には出していません。次に、チケット作成で使うトラッカーと優先度のIDを確かめます。
curl -s -H "X-Redmine-API-Key: $(cat api-key.txt)" http://127.0.0.1:18080/trackers.json | jq -c '.trackers[] | {id, name, default_status: .default_status.name}'
curl -s -H "X-Redmine-API-Key: $(cat api-key.txt)" http://127.0.0.1:18080/enumerations/issue_priorities.json | jq -c '.issue_priorities[] | {id, name}'{"id":1,"name":"バグ","default_status":"新規"}
{"id":2,"name":"機能","default_status":"新規"}
{"id":3,"name":"サポート","default_status":"新規"}
{"id":1,"name":"低め"}
{"id":2,"name":"通常"}
{"id":3,"name":"高め"}
{"id":4,"name":"急いで"}
{"id":5,"name":"今すぐ"}プロジェクトの定義は project.json に書きます。is_public を false にして、メンバー以外には見えないプロジェクトにします。
{
"project": {
"name": "社内ITヘルプデスク",
"identifier": "it-helpdesk",
"description": "PCや社内システムの依頼と障害を受け付けるプロジェクト",
"is_public": false
}
}curl -s -H "X-Redmine-API-Key: $(cat api-key.txt)" -H 'Content-Type: application/json' \
-d @project.json http://127.0.0.1:18080/projects.json \
| jq -c '.project | {id, identifier, name, is_public}'{"id":1,"identifier":"it-helpdesk","name":"社内ITヘルプデスク","is_public":false}添付付きのチケットは2段階です。まずファイルの中身を /uploads.json に送ってトークンを受け取り、チケット作成のJSONにそのトークンを入れます。アップロードは Content-Type: application/octet-stream が必須で、付けずに送ると 406 が返りました(APIリファレンスの記載どおりです)。
printf 'ノートPCキッティング手順(例)\n1. 資産タグを台帳に転記\n2. OSの更新を適用\n3. ディスク暗号化を有効化\n' > kitting-checklist.txt
curl -s -H "X-Redmine-API-Key: $(cat api-key.txt)" -H 'Content-Type: application/octet-stream' \
--data-binary @kitting-checklist.txt 'http://127.0.0.1:18080/uploads.json?filename=kitting-checklist.txt' \
| jq -r .upload.token > upload-token.txt
jq -n --arg token "$(cat upload-token.txt)" \
'{issue: {project_id: "it-helpdesk", tracker_id: 3, subject: "新入社員用ノートPCのキッティング", description: "手順は添付のチェックリストを参照。", uploads: [{token: $token, filename: "kitting-checklist.txt", content_type: "text/plain"}]}}' \
> issue.json
curl -s -H "X-Redmine-API-Key: $(cat api-key.txt)" -H 'Content-Type: application/json' \
-d @issue.json http://127.0.0.1:18080/issues.json \
| jq -c '.issue | {id, tracker: .tracker.name, status: .status.name, priority: .priority.name, subject}'{"id":1,"tracker":"サポート","status":"新規","priority":"通常","subject":"新入社員用ノートPCのキッティング"}tracker_id の 3 は上で確かめた「サポート」です。続けて、バグを優先度「高め」で1件、機能要望を1件登録します。検証では、プロジェクトのメンバーに入れていない管理者のままチケットを作れました。メンバーとロールの割り当ては運用を始めるときに画面で行う作業で、この記事では扱いません。
curl -s -H "X-Redmine-API-Key: $(cat api-key.txt)" -H 'Content-Type: application/json' \
-d '{"issue": {"project_id": "it-helpdesk", "tracker_id": 1, "priority_id": 3, "subject": "会議室Aの無線LANが途切れる"}}' \
http://127.0.0.1:18080/issues.json \
| jq -c '.issue | {id, tracker: .tracker.name, status: .status.name, priority: .priority.name, subject}'
curl -s -H "X-Redmine-API-Key: $(cat api-key.txt)" -H 'Content-Type: application/json' \
-d '{"issue": {"project_id": "it-helpdesk", "tracker_id": 2, "subject": "勤怠システムにシングルサインオンを追加"}}' \
http://127.0.0.1:18080/issues.json \
| jq -c '.issue | {id, tracker: .tracker.name, status: .status.name, priority: .priority.name, subject}'{"id":2,"tracker":"バグ","status":"新規","priority":"高め","subject":"会議室Aの無線LANが途切れる"}
{"id":3,"tracker":"機能","status":"新規","priority":"通常","subject":"勤怠システムにシングルサインオンを追加"}動作確認
チケットの一覧、添付ファイルの中身、未ログインのときの応答の3点を確かめます。
curl -s -H "X-Redmine-API-Key: $(cat api-key.txt)" \
'http://127.0.0.1:18080/issues.json?project_id=it-helpdesk&status_id=*&sort=id' \
| jq -r '"total_count=\(.total_count)", (.issues[] | "\(.id) \(.tracker.name) \(.status.name) \(.priority.name) \(.subject)")'
curl -s -H "X-Redmine-API-Key: $(cat api-key.txt)" 'http://127.0.0.1:18080/issues/1.json?include=attachments' \
| jq -c '.issue.attachments[] | {id, filename, filesize}'
curl -s -H "X-Redmine-API-Key: $(cat api-key.txt)" http://127.0.0.1:18080/attachments/download/1/kitting-checklist.txt | sha256sum
sha256sum < kitting-checklist.txt
for p in /login /account/register /account/lost_password /projects/it-helpdesk /trackers.json /issues.json; do curl -s -o /dev/null -w "%{http_code} $p %{redirect_url}\n" "http://127.0.0.1:18080$p"; donetotal_count=3
1 サポート 新規 通常 新入社員用ノートPCのキッティング
2 バグ 新規 高め 会議室Aの無線LANが途切れる
3 機能 新規 通常 勤怠システムにシングルサインオンを追加
{"id":1,"filename":"kitting-checklist.txt","filesize":140}
961e152c1222c1332769254d2f937d1e104944e6b7c1d45cf177b2d4600200f0 -
961e152c1222c1332769254d2f937d1e104944e6b7c1d45cf177b2d4600200f0 -
200 /login
200 /account/register
200 /account/lost_password
302 /projects/it-helpdesk http://127.0.0.1:18080/login?back_url=http%3A%2F%2F127.0.0.1%3A18080%2Fprojects%2Fit-helpdesk
401 /trackers.json
401 /issues.json total_count=3 と3行のチケットが日本語のトラッカー名・ステータス名で返り、ダウンロードした添付と手元のファイルの SHA-256 が一致していれば、登録までの手順は通っています。最後の6行は、APIキーも Cookie も付けない未ログインでのアクセスです。API の /trackers.json と /issues.json は 401、プロジェクトのページは 302 で /login へ転送され、試した6つのうち 200 が返ったのはログイン画面、アカウント登録、パスワード再発行の3つでした。
画面で見る場合は、サーバー上のブラウザで http://127.0.0.1:18080/ を開き、作成した管理者のログインID(例では ryoma)と admin-password.txt の中身でログインして、http://127.0.0.1:18080/projects/it-helpdesk/issues へ進みます。検証では同じログインを curl のセッションで行い、ページのタイトルが「チケット - 社内ITヘルプデスク - Redmine」、一覧の行が 3 つ、更新日の列が日本時間であることを確認しました。127.0.0.1 に限定したポートへは別のPCから直接届かないので、手元のPCから見るにはSSHのポートフォワードかリバースプロキシを経由します(どちらもこの検証では使っていません)。
メモリとディスクの実測値
docker stats --no-stream --format 'table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}' $(docker compose ps -q)
docker compose exec db psql -U redmine -d redmine -Atc "select pg_size_pretty(pg_database_size('redmine'))"
docker compose exec redmine du -sh files
docker images --format '{{.Repository}}:{{.Tag}}\t{{.Size}}' | grep -E '^(redmine|postgres):'NAME MEM USAGE / LIMIT MEM %
redmine-docker-compose-setup-db-1 40.19MiB / 512MiB 7.85%
redmine-docker-compose-setup-redmine-1 203MiB / 1GiB 19.82%
11 MB
16K files
redmine:7.0.2 1.05GB
postgres:17.11 646MB初期設定とチケット3件の登録を終えた直後、ほかにアクセスのない状態での値です。メモリは Redmine が203MiB、PostgreSQL が40.19MiBで、合計は約243MiBでした。DBは 11MB、添付の入った files は 16K です。イメージは docker images の表示で 1.05GB と 646MB なので、ディスクはデータよりイメージの分を先に見込みます。利用者とチケットが増えればメモリもDBも増えますが、どこまで増えるかはこの検証では測っていません。
バックアップを取り、別のスタックに復元して照合する
Redmine公式のバックアップ手順が対象に挙げているのは、データベースと添付ファイルのディレクトリの2つです。取るだけで終わらせず、空のスタックに戻して中身を比べるところまでを1セットにします。
バックアップを取る
install -d -m 700 backup
docker compose exec -T db pg_dump -U redmine -Fc redmine > backup/redmine.dump
docker compose exec -T redmine tar -C /usr/src/redmine -czf - files > backup/redmine-files.tar.gz
cp compose.yaml backup/
wc -c backup/redmine.dump backup/redmine-files.tar.gz backup/compose.yaml
docker compose exec -T db pg_restore --list < backup/redmine.dump | grep -c 'TABLE DATA'
tar -tzf backup/redmine-files.tar.gz
docker compose exec db psql -U redmine -d redmine -Atc "select 'issues', count(*) from issues union all select 'attachments', count(*) from attachments union all select 'projects', count(*) from projects union all select 'users', count(*) from users where type = 'User'"168241 backup/redmine.dump
362 backup/redmine-files.tar.gz
1274 backup/compose.yaml
169877 total
60
files/
files/2026/
files/2026/10/
files/2026/10/261010073213_kitting-checklist.txt
issues|3
attachments|1
projects|1
users|2pg_restore --list はダンプの目次を表示するコマンドで、上の 60 は目次に TABLE DATA の項目が 60 あるという意味です。この確認がどこまでを見ているかを、ダンプの末尾を削ったデータで確かめておきます。ダンプにはパスワードハッシュとAPIキーが入っているので、削ったコピーをファイルには残さず、パイプで渡します。
head -c -5000 backup/redmine.dump | docker compose exec -T db pg_restore --list | grep -c 'TABLE DATA'60head -c -5000 で末尾を削ったデータでも、結果は同じ 60 でした。この数が示すのは、目次が読めて項目の数が合うところまでです。データ部分が最後まで書けているかどうかは、この確認ではわかりません。データまで戻せることは、次の「空のスタックに復元する」で実際に復元して確かめます。
pg_dump -Fcはpg_restoreで戻すためのカスタム形式です。- DBのコンテナの中から接続するときにパスワードを聞かれないのは、公式イメージがコンテナ内のローカル接続を trust 認証にしているためです。
-Tは、リダイレクトでバイナリを受け取るために端末の割り当てを止める指定です。 backupディレクトリは権限 700 で作ります。ダンプにはユーザーのパスワードハッシュとAPIキーが入っており、実際に復元先では同じパスワードとAPIキーがそのまま通りました。.envはバックアップに入れていません。復元先ではDBのパスワードもSECRET_KEY_BASEも新しく生成しましたが、データは読めました。持ち出す秘密を1つ減らせます。- アプリを動かしたまま取っているので、ダンプと tar のあいだに添付が追加・削除されると両者が食い違います。DBを先に取っておけば、ダンプに載っている添付のファイルは、その後に削除されない限り tar に入ります。利用者のいない時間帯に実行するのが手軽な対策で、Redmine を止めてから取る方法は試していません。
backup は Redmine と同じディスクの上にあります。ディスク障害や、誤って実行した docker compose down -v からは守れません。別のホストへ運ぶところまでがバックアップで、運び方はresticでLinuxサーバーをバックアップするの手順が使えます。
空のスタックに復元する
復元先は別のディレクトリに作ります。compose.yaml はバックアップから取り出し、.env はプロジェクト名とポートを変えて作り直します。
mkdir -p ~/redmine-restore && cd ~/redmine-restore
cp ~/redmine/backup/compose.yaml .
install -m 600 /dev/null .env
cat > .env <<EOF
COMPOSE_PROJECT_NAME=redmine-docker-compose-setup-restore
REDMINE_PORT=18081
POSTGRES_PASSWORD=$(openssl rand -hex 16)
SECRET_KEY_BASE=$(openssl rand -hex 64)
EOF復元は順番で結果が変わります。先にDBだけを起動してダンプを流し込み、その後で Redmine を起動してください。逆にすると、空のDBにマイグレーションが走ってテーブルと初期データができ、復元と衝突します(「つまずきポイント」で再現します)。
docker compose up -d --wait db
docker compose exec -T db pg_restore -U redmine -d redmine < ~/redmine/backup/redmine.dump
docker compose up -d
SECONDS=0; until curl -fs -o /dev/null http://127.0.0.1:18081/; do sleep 2; done; echo "応答まで ${SECONDS} 秒"
docker compose logs redmine | grep ': migrated' | wc -l
docker compose exec -T redmine tar -C /usr/src/redmine -xzf - < ~/redmine/backup/redmine-files.tar.gz
docker compose exec redmine find files -type f -printf '%u:%g %s %f\n'応答まで 11 秒
0
redmine:redmine 140 261010073213_kitting-checklist.txtpg_restore は何も出力せずに終わり、Redmine は11秒で応答しました。同じ版のダンプなので、起動時のマイグレーションは 0 です。添付は Redmine のコンテナに tar を流し込んで展開し、所有者が redmine のまま戻っていることを確かめています。
件数とハッシュで照合する
docker compose exec db psql -U redmine -d redmine -Atc "select 'issues', count(*) from issues union all select 'attachments', count(*) from attachments union all select 'projects', count(*) from projects union all select 'users', count(*) from users where type = 'User'"
curl -s -H "X-Redmine-API-Key: $(cat ~/redmine/api-key.txt)" \
'http://127.0.0.1:18081/issues.json?project_id=it-helpdesk&status_id=*&sort=id' \
| jq -r '"total_count=\(.total_count)", (.issues[] | "\(.id) \(.tracker.name) \(.status.name) \(.priority.name) \(.subject)")'
curl -s -H "X-Redmine-API-Key: $(cat ~/redmine/api-key.txt)" http://127.0.0.1:18081/attachments/download/1/kitting-checklist.txt | sha256sum
sha256sum < ~/redmine/kitting-checklist.txt
curl -s -o /dev/null -w '%{http_code}\n' -u "ryoma:$(cat ~/redmine/admin-password.txt)" http://127.0.0.1:18081/users/current.jsonissues|3
attachments|1
projects|1
users|2
total_count=3
1 サポート 新規 通常 新入社員用ノートPCのキッティング
2 バグ 新規 高め 会議室Aの無線LANが途切れる
3 機能 新規 通常 勤怠システムにシングルサインオンを追加
961e152c1222c1332769254d2f937d1e104944e6b7c1d45cf177b2d4600200f0 -
961e152c1222c1332769254d2f937d1e104944e6b7c1d45cf177b2d4600200f0 -
200| 照合する項目 | 元のスタック(18080) | 復元先(18081) |
|---|---|---|
| チケット(issues) | 3 | 3 |
| 添付(attachments) | 1 | 1 |
| プロジェクト(projects) | 1 | 1 |
| ユーザー(users) | 2 | 2 |
| 添付の SHA-256 | 961e152c1222c133… | 961e152c1222c133… |
| 元の管理者パスワードでのBasic認証 | APIキーの取得に使用 | HTTP 200 |
件数は4項目とも一致し、復元先からダウンロードした添付のハッシュも元のファイルと同じでした。チケット一覧は元のAPIキーで取得したもので、DBのパスワードと SECRET_KEY_BASE を新しくした復元先でも、元のAPIキーと管理者のパスワードがそのまま通っています。イメージの説明によれば SECRET_KEY_BASE が変わると既存のログインセッションは無効になるので、切り替え後に利用者が再ログインを求められる点だけ見込んでおきます(セッションの失効そのものは試していません)。
つまずきポイント
ここからの4つは、復元先のスタック(~/redmine-restore、ポート 18081)でわざと起こしたものです。データを壊す操作を含むので、本番のスタックでは実行しないでください。
デフォルトデータの投入が NoMethodError で止まる
REDMINE_LANG を付けず、端末のない状態で rake タスクを流すと例外で止まります。スクリプトやCIから実行したときに踏む形です。
docker compose exec -T -u redmine redmine bundle exec rake redmine:load_default_data < /dev/null 2>&1 | grep -o 'NoMethodError.*'NoMethodError: undefined method 'chomp!' for nil (NoMethodError)タスクは言語を標準入力から読もうとし、入力がないので nil に chomp! を呼んで落ちています。-e REDMINE_LANG=ja で渡せば入力待ちになりません。なお、投入済みのDBに別の言語でもう一度流しても、名前は変わりませんでした。
docker compose exec -u redmine -e REDMINE_LANG=en redmine bundle exec rake redmine:load_default_data
curl -s -H "X-Redmine-API-Key: $(cat ~/redmine/api-key.txt)" http://127.0.0.1:18081/trackers.json | jq -c '[.trackers[].name]'Some configuration data is already loaded.
["バグ","機能","サポート"]言語を間違えて投入したら、チケットを作る前ならボリュームごと作り直すのが手早い方法です。運用開始後は、トラッカーやステータスの名前を管理画面で1つずつ直すことになります(名前の修正はこの記事では試していません)。
正しいAPIキーなのに 403 が返る
echo 'Setting.rest_api_enabled = "0"' | docker compose exec -T -u redmine redmine bin/rails runner -
curl -s -o /dev/null -w '%{http_code}\n' -H "X-Redmine-API-Key: $(cat ~/redmine/api-key.txt)" http://127.0.0.1:18081/users/current.json
echo 'Setting.rest_api_enabled = "1"' | docker compose exec -T -u redmine redmine bin/rails runner -
curl -s -o /dev/null -w '%{http_code}\n' -H "X-Redmine-API-Key: $(cat ~/redmine/api-key.txt)" http://127.0.0.1:18081/users/current.json403
200rest_api_enabled が 0 のあいだは、キーが正しくても 403 でした。初期値が 0 なので、init-settings.rb を流していない環境ではこうなり、1 に戻すと同じキーで 200 が返っています。この検証で 403 を見たのは「REST APIが無効」のときと「APIは有効で、パスワード変更が済んでいない admin」のとき、401 を見たのは「APIが有効な状態でのパスワードの誤り」「ロック済みのアカウント」「login_required を有効にしたスタックでの未ログイン」のときです。権限のないユーザーが正しい資格情報で呼んだ場合にどちらが返るかは、この検証では試していません。
.env を作り直したら Redmine が応答しなくなる
.env の作成コマンドをやり直した状況を、パスワードの行だけ書き換えて再現します。
sed -i "s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$(openssl rand -hex 16)/" .env
docker compose up -d
sleep 20; docker compose logs redmine | grep -o -m1 'FATAL:.*'
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:18081/loginFATAL: password authentication failed for user "redmine"
000POSTGRES_PASSWORD などの初期化用の変数は、データディレクトリが空のときにしか効きません(PostgreSQL公式イメージの説明にある注意です)。ボリュームには最初のパスワードが残り、Redmine だけが新しい値で接続しに行くので認証に失敗します。.env を元に戻せないときは、DB側のパスワードを .env に合わせます。
docker compose exec db psql -U redmine -d redmine -c "ALTER ROLE redmine PASSWORD '$(grep '^POSTGRES_PASSWORD=' .env | cut -d= -f2)'"
SECONDS=0; until curl -fs -o /dev/null http://127.0.0.1:18081/; do sleep 2; done; echo "応答まで ${SECONDS} 秒"
curl -s -H "X-Redmine-API-Key: $(cat ~/redmine/api-key.txt)" http://127.0.0.1:18081/attachments/download/1/kitting-checklist.txt | sha256sumALTER ROLE
応答まで 11 秒
961e152c1222c1332769254d2f937d1e104944e6b7c1d45cf177b2d4600200f0 -ALTER ROLE の後は何も再起動していません。restart: unless-stopped で起動を繰り返していたコンテナが、次の試行で接続に成功しています。up -d で2つのコンテナは作り直されましたが、添付のハッシュは変わっておらず、名前付きボリュームのデータが残っていることも確かめられました。
復元したのにユーザーや設定が欠けている
復元の順番を逆にして、Redmine を先に起動してからダンプを流します。
docker compose down -v
docker compose up -d
SECONDS=0; until curl -fs -o /dev/null http://127.0.0.1:18081/; do sleep 2; done; echo "応答まで ${SECONDS} 秒"
docker compose exec -T db pg_restore -U redmine -d redmine < ~/redmine/backup/redmine.dump > restore-wrong.log 2>&1; tail -n 1 restore-wrong.log
grep -o 'COPY failed for table "[a-z_]*"' restore-wrong.log
docker compose exec db psql -U redmine -d redmine -Atc "select 'issues', count(*) from issues union all select 'attachments', count(*) from attachments union all select 'projects', count(*) from projects union all select 'users', count(*) from users where type = 'User'"応答まで 12 秒
pg_restore: warning: errors ignored on restore: 300
COPY failed for table "ar_internal_metadata"
COPY failed for table "email_addresses"
COPY failed for table "roles"
COPY failed for table "schema_migrations"
COPY failed for table "settings"
COPY failed for table "users"
issues|3
attachments|1
projects|1
users|1マイグレーションで先にできていたテーブルや初期データと衝突し、pg_restore の最後の行は errors ignored on restore: 300 でした。厄介なのは、中途半端に成功することです。空だった issues には3行が入りましたが、初期データのある users、roles、settings など6つのテーブルは COPY が丸ごと失敗し、users は組み込みの admin だけの 1 のままです。チケットの件数だけを見ると成功に見えるので、照合にはユーザー数も入れます。直すには docker compose down -v で消してから、DBだけ起動、pg_restore、全体の起動、の順でやり直します。
消す前に、このスタックがどんな状態で動いているかを check-state.rb と未ログインの curl で見ておきます。
docker compose exec -T -u redmine redmine bin/rails runner - < ~/redmine/check-state.rb
for p in /login /account/register /account/lost_password /projects/it-helpdesk /trackers.json /issues.json; do curl -s -o /dev/null -w "%{http_code} $p %{redirect_url}\n" "http://127.0.0.1:18081$p"; done
curl -s http://127.0.0.1:18081/issues.json | jq -c '{total_count}'trackers=3 statuses=6 priorities=5 roles=0
admin: locked=false must_change_passwd=true
rest_api_enabled=0 login_required=0
default_language=en default_users_time_zone=""
host_name=localhost:3000
200 /login
200 /account/register
200 /account/lost_password
302 /projects/it-helpdesk http://127.0.0.1:18081/login?back_url=http%3A%2F%2F127.0.0.1%3A18081%2Fprojects%2Fit-helpdesk
200 /trackers.json
200 /issues.json
{"total_count":0}設定は初回起動のときと同じ値で、login_required=0、rest_api_enabled=0 です。組み込みの admin は locked=false で、実際に admin / admin のままログインでき、パスワード変更の画面へ進みました。未ログインの /trackers.json と /issues.json は 200 を返します(/issues.json の total_count は 0、プロジェクトのページは /login へ転送でした)。トラッカーやステータスは入っていますが、ロールは 0 です。復元したつもりのスタックが、初期パスワードの管理者を受け付けたまま動いているので、照合に失敗したらその場で消します。
docker compose down -vこれで復元先のスタックはボリュームごと消えます。
本番サーバーで変わるところと、試していないこと
この記事の検証は、1台のサーバーの 127.0.0.1 だけで完結しています。社内に出すときに変わる点と、実行していない範囲は次のとおりです。
- TLSとリバースプロキシ:他のPCから使うなら、nginxのリバースプロキシなどでHTTPSを終端し、127.0.0.1:18080 へ中継します。この構成は試していません。あわせて「ホスト名とパス」の設定が
localhost:3000のままなので(check-state.rbの最終行)、通知メールに書かれるURLのために実際のホスト名へ直す必要があります。 - メール通知:送信設定をしていないので、通知メールは出ません。検証環境では ActionMailer の
perform_deliveriesがfalseでした。日本語ロケールのメッセージはconfig/configuration.ymlにSMTPサーバーを設定して再起動するよう案内しています。このファイルをコンテナに渡す構成は試していません。 - バックアップの定期実行:cron や systemd timer からの実行は試していません。端末のない環境で動かすことになるので、この記事のバックアップコマンドは最初から
-T付きにしてあります。 - バージョンアップ:イメージのタグを上げて起動し直すと、起動時に
db:migrateが走る仕組みです。公式のアップグレード手順は、先にDBと添付をバックアップするよう勧めています。タグを上げる更新そのものは試していません。 - ホストの再起動、負荷、プラグイン:ホスト再起動後の自動起動、利用者が増えたときのメモリ、プラグインを入れた構成は、いずれも確認していません。
検証だけで終える場合の片付け
試すだけで終えるなら、~/redmine に戻って元のスタックも消します。-v はボリュームごと、つまりチケットと添付をすべて削除します。使い続ける環境では実行しないでください。docker rmi は、同じイメージをほかで使っていない場合だけ実行します。
cd ~/redmine
docker compose down -v
docker rmi redmine:7.0.2 postgres:17.11自前運用・ホスティング・SaaSを運用の手間で比べる
Redmine を使う方法は、ここまでのように自分で動かす以外に、事業者が運用する Redmine を借りる方法もあります。製品を Redmine に限らないなら、SaaS型の課題管理ツールも候補です。金額は各社の料金ページに任せ、ここでは「誰が何をやるか」で比べます。
| 作業 | 自前で運用(この記事の構成) | Redmineのホスティングサービス | SaaS型の課題管理ツール |
|---|---|---|---|
| 初期構築 | compose.yaml、.env、初期設定のスクリプトを用意して実行 | 申し込み後、画面で初期設定 | 申し込み後、画面で初期設定 |
| 版の更新 | タグの変更と事前のバックアップを自分で行う | 事業者が実施。時期を選べるかは契約前に確認 | 事業者が実施 |
| バックアップと復元の確認 | pg_dump と添付を自分で取り、復元も自分で試す | 事業者が取得。データを手元に取り出せるか、復元を依頼できるかを確認 | 事業者が取得。エクスポートの形式と範囲を確認 |
| 公開範囲と認証 | リバースプロキシ、TLS証明書、認証の設定を自分で持つ | 事業者の提供範囲。接続元の制限やシングルサインオンの有無を確認 | プランごとの機能を確認 |
| プラグインと改造 | 制限なし。動作確認は自分で行う | 事業者が認める範囲 | 製品が用意する拡張の範囲 |
| 障害の切り分け | 自分で行う(「つまずきポイント」のような調査) | 事業者に問い合わせる | 事業者に問い合わせる |
| 費用の出方 | ライセンス料はなし。サーバーと、運用する人の時間 | 利用料 | 利用料 |
| データの置き場所 | 自分のサーバー | 事業者の設備 | 事業者の設備 |
判断の軸は、この記事の「バックアップを取り、別のスタックに復元して照合する」を定期的に流す担当者を置けるかどうかです。置けるなら自前運用を選べますし、閉じたネットワークの中に置きたい、プラグインを自由に入れたい、という要件にも応えられます。置けないなら、Redmine の画面や運用に慣れた組織はホスティングサービス、そうでなければSaaS型を選ぶほうが、復元を試したことのないバックアップを抱え続けるよりましです。逆に、データを社外に出せない事情があるなら、手間を承知のうえで自前運用が残る選択肢になります。
よくある質問
Redmineは無料で使えますか?
使えます。Redmine本体は GNU GPL v2 で公開されているオープンソースソフトウェアで、ライセンス料なしで入手できます。自分のサーバーで動かす場合にかかるのは、サーバーの費用と、更新やバックアップを担当する人の時間です。
Redmineの料金はいくらですか?
本体に料金はありません。自前で動かす場合の負担はサーバーと運用の手間で、この記事の構成ではメモリが2コンテナ合計で約243MiBでした。事業者が運用するホスティングサービスには利用料がかかり、金額はプランや利用人数で変わるので、各社の料金ページで確認してください。
RedmineとJiraの違いは何ですか?
提供のされ方が違います。Redmine は GPL v2 のオープンソースで、自分のサーバーに入れて運用するのが基本です。Jira は Atlassian 社の製品で、執筆時点の料金ページには Free、Standard、Premium、Enterprise の4プランが並んでいます。運用を自分で持つか、利用料を払って任せるかが最初の分かれ目になります。
Redmineとは何ですか?
チケット単位で作業を追跡する、オープンソースのプロジェクト管理Webアプリケーションです。公式サイトは主な機能として、複数プロジェクトの管理、ロールに基づくアクセス制御、ガントチャートとカレンダー、プロジェクトごとのWiki、作業時間の記録などを挙げています。
まとめ
起動そのものは compose.yaml と .env があれば up -d から十数秒で済みますが、使える状態にするには設定、管理者、デフォルトデータの3つが要ります。ここをスクリプトと rake にしておくと、環境を作り直すたびに画面を操作し直さずに済みます。運用で効いてくるのは、復元の順番(DBだけ起動、pg_restore、全体の起動)と、ユーザー数まで含めた件数の照合です。次にやることは、検証用のディレクトリで動作確認まで通し、そのまま復元先を作って照合を1回やり切ることです。本番に進むのはその照合が通ってからにし、社内に出す段階で TLS のリバースプロキシ、メール設定、バックアップの定期実行と別ホストへの持ち出しを足してください。
次に読む
- nginxでリバースプロキシを構築する:127.0.0.1 のRedmineを社内にHTTPSで出すための中継の作り方
- resticでLinuxサーバーをバックアップする:backup ディレクトリを別のホストへ運び、リストアを検証する手順
- プロジェクト管理ツールの選び方:Redmine以外の候補を含めて、必要な機能と比較軸を整理