---
title: "GitBucketをDockerで構築する手順とJenkins連携の落とし穴"
url: "https://www.issoh.co.jp/tech/details/3808/"
published: 2024-10-10
updated: 2026-08-02
categories: ["アーキテクチャ"]
publisher: "株式会社一創"
---

# GitBucketをDockerで構築する手順とJenkins連携の落とし穴

GitBucketをDockerで動かす記事の多くは `docker run gitbucket/gitbucket` から始まります。ところがこのDocker Hubのイメージは、2022年12月25日のGitBucket 4.38.4を最後に更新が止まっています。本家は2026年4月18日に4.46.1をリリースしており、そのイメージはGitHub Container Registry（ghcr.io）で配布されています。本稿では配布先の移行を踏まえた構築手順と、Jenkinsと連携させるときにプラグイン選定でつまずくポイントを、公式リポジトリとJenkinsのセキュリティ情報をもとに整理しました。

## まとめ

作業前に押さえるべき結論は次の5点です。

| 論点          | 結論                                                                                           |
| ----------- | -------------------------------------------------------------------------------------------- |
| 使うイメージ      | `ghcr.io/gitbucket/gitbucket:4.46.1`（Docker Hub側は4.38.4で停止）                                  |
| 永続化         | コンテナ内 `/gitbucket` を名前付きボリュームへ。ここにDB・Gitリポジトリ・プラグインが入る                                       |
| Jenkins側    | `jenkins/jenkins:lts`（2026年8月時点で2.568.1）。JENKINS\_HOMEは `/var/jenkins_home`                  |
| 連携プラグイン     | GitBucket Plugin（0.8）とGitHub Pull Request Builder（1.42.2）は脆弱性が未修正。Generic Webhook Triggerを使う |
| Webhookの形式  | 既定はform-urlencoded。JSONPathで値を取るならContent typeを `application/json` へ変更                       |
| プルリクエストのマージ | `push` と `pull_request`（closed）の両方が飛ぶ。マージだけを拾うなら `merged` が true か判定                         |

GitBucketはScalaで書かれたGitホスティングサーバーで、Issue・プルリクエスト・Wiki・GitHub互換のREST APIを1つのwarファイルで提供します。単体で完結するためDockerとの相性が良く、社内向けのGitサーバーを最短で立てたい場面で選ばれます。

## Docker HubとGHCRで割れた公式イメージの現状

GitBucketのDockerイメージは配布先が2つに割れており、片方が3年半以上放置されています。両レジストリのタグ情報は次のとおりです（2026年8月2日時点）。

| 配布先                           | latestの中身 | 最新リリースタグの公開日 | 置かれているバージョンタグ                                        |
| ----------------------------- | --------- | ------------ | ---------------------------------------------------- |
| docker.io/gitbucket/gitbucket | 4.38.4    | 2022-12-25   | 4.29.0〜4.38.4の13タグ、ほかに3・4・latest・master・nightly（全18） |
| ghcr.io/gitbucket/gitbucket   | 4.46.1    | 2026-04-19   | 4.39.0〜4.46.1、ほかにmaster・nightly                      |

移行の理由はビルド定義そのものにあります。公式イメージのビルドリポジトリ gitbucket/gitbucket-docker のGitHub Actionsワークフローは、プッシュ先を `ghcr.io/gitbucket/gitbucket` だけに指定しており、Docker Hubへプッシュする処理を持ちません。4.39.0以降のタグがDocker Hub側に増えていないのは、そのためです。

「なぜかバージョンが古い」「新しい機能の説明どおりに動かない」という詰まり方をしたら、まず自分が引いたイメージの出所を疑ってください。ghcr.ioの `latest` は4.46.1と同一ダイジェスト（sha256:e30a8a61…）を指しており、本家リリースに追随しています。amd64とarm64の両方がビルドされているため、Apple Siliconでもエミュレーションなしで動きます。

### イメージの中身と公開ポート

4.46.1のイメージ構成は次のとおりです。ベースは `eclipse-temurin:17` で、GitBucket本体がJava 17を必須にしている要件と一致します。

```
CMD     sh -c "java -jar /opt/gitbucket.war"
EXPOSE  8080/tcp, 29418/tcp
VOLUME  /gitbucket
```

29418はGit over SSH用のポートです。ポートを開けただけでは有効になりません。管理画面の Administration にある System Settings でSSHアクセスを有効化し、あわせてBase URLを設定する必要があります。SSHを使わずHTTP経由でcloneするだけなら、8080だけの公開で構いません。

## docker runでの最短起動とデータの置き場所

まず単体で挙動を確かめる場合は、ボリュームを1つ与えるだけで起動します。

```
docker run -d --name gitbucket \
  -p 8080:8080 \
  -v gitbucket_home:/gitbucket \
  ghcr.io/gitbucket/gitbucket:4.46.1
```

ブラウザで `http://localhost:8080/` を開き、ID `root` ／ パスワード `root` でログインします。初回ログイン後にパスワードを変更してください。

永続化の対象は `/gitbucket` ひとつだけです。ここに設定ファイル、H2データベース、Gitリポジトリの実体、追加プラグインがまとめて入ります。イメージ側では `/root/.gitbucket` が `/gitbucket` へのシンボリックリンクになっているため、データディレクトリを意識せずにボリュームを当てられます。バインドマウントにするとホスト側のパーミッション次第で書き込みに失敗するので、名前付きボリュームが無難です。[run・start・execの使い分けや起動しない場合の確認手順](https://www.issoh.co.jp/tech/details/3935/)は別記事にまとめています。

設定はコマンドライン引数と環境変数のどちらでも渡せます。Dockerでは環境変数のほうが扱いやすく、対応関係は次のとおりです。

| CLIオプション               | 環境変数                         | 既定値           |
| ---------------------- | ---------------------------- | ------------- |
| `--gitbucket.home`     | `GITBUCKET_HOME`             | \~/.gitbucket |
| `--port`               | `GITBUCKET_PORT`             | 8080          |
| `--prefix`             | `GITBUCKET_PREFIX`           | /             |
| `--connectors`         | `GITBUCKET_CONNECTORS`       | http          |
| `--jetty_idle_timeout` | `GITBUCKET_JETTYIDLETIMEOUT` | 300000（5分）    |

## Docker ComposeでGitBucketとJenkinsを並べる構成

CI連携まで見据えるなら、最初からComposeで2コンテナを定義したほうが手戻りがありません。次が最小構成です。`version` のトップレベル要素はCompose仕様で廃止扱いになっており、書いても警告が出るだけなので入れません。

```
services:
  gitbucket:
    image: ghcr.io/gitbucket/gitbucket:4.46.1
    ports:
      - "8080:8080"
      - "29418:29418"
    volumes:
      - gitbucket_home:/gitbucket
    restart: unless-stopped

  jenkins:
    image: jenkins/jenkins:lts
    ports:
      - "8081:8080"
      - "50000:50000"
    volumes:
      - jenkins_home:/var/jenkins_home
    restart: unless-stopped

volumes:
  gitbucket_home:
  jenkins_home:
```

両方ともコンテナ内では8080で待ち受けるため、ホスト側でJenkinsを8081にずらしています。同じComposeプロジェクト内のサービスはサービス名で名前解決できるので、GitBucketからJenkinsを呼ぶURLは `http://jenkins:8080/` です。ホスト側のポート番号（8081）ではありません。[docker-composeで複数コンテナをymlに定義して一括管理する仕組み](https://www.issoh.co.jp/tech/details/13282/)は別記事で解説しています。

### Jenkins側の初期セットアップ

起動後、Jenkinsの初期管理者パスワードはコンテナ内のファイルから取り出します。

```
docker compose up -d
docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
```

`jenkins/jenkins:lts` は2026年8月時点でJenkins 2.568.1を指しています。イメージ内のJENKINS\_HOMEは `/var/jenkins_home`、公開ポートは8080（Web UI）と50000（エージェント接続用）、実行ユーザーは `jenkins` です。50000番はエージェントを別コンテナやマシンに置くときだけ必要で、単体運用なら閉じて構いません。[Jenkins本体のインストール方法と初期設定](https://www.issoh.co.jp/tech/details/3957/)は別記事を参照してください。

## Jenkins連携プラグインの現況と、避けるべき2つ

古い解説記事と現実が最も乖離しているのがこの部分です。「GitBucket Plugin を入れる」「GitHub Pull Request Builder でプルリクエストをビルドする」と書かれた手順は、いま新規に採用すべきではありません。Jenkinsの更新センターが配信しているプラグイン情報とセキュリティ警告を照合すると、次の状態です。

| プラグイン                              | 最新版    | リリース       | 状態                                       |
| ---------------------------------- | ------ | ---------- | ---------------------------------------- |
| GitBucket Plugin（gitbucket）        | 0.8    | 2015-09-10 | SECURITY-3249（Stored XSS）が全バージョン該当・修正版なし |
| GitHub Pull Request Builder（ghprb） | 1.42.2 | 2021-02-13 | deprecated指定。SECURITY-2789の2件が全バージョン該当   |
| Generic Webhook Trigger            | 2.4.2  | 2026-05-16 | 現行メンテナンス中。必要コアは2.479.3以上                 |

GitBucket Pluginが要求するJenkinsコアは1.609.3で、これは2015年当時のバージョンです。約11年更新されておらず、2024年3月6日のアドバイザリで報告されたStored XSSも未修正のまま残っています。ghprbのほうはプラグインサイト上で「GitHub Branch Source Plugin に置き換えられた」と明記され、既知の脆弱性を抱えたままメンテナンスが終了しました。

では何を使うか。GitBucketはGitHub互換APIを持つため、選択肢は実質2つに絞られます。

| 方式               | 使うプラグイン                                    | 向く場面                          |
| ---------------- | ------------------------------------------ | ----------------------------- |
| Webhookで直接ジョブを起動 | Generic Webhook Trigger                    | リポジトリ数が少なく、ポート構成を変えたくない       |
| ブランチ・組織を自動検出     | GitHub Branch Source（Multibranch Pipeline） | ブランチやリポジトリが多く、ジョブ定義を手で増やしたくない |

Dockerで手早く立てた環境なら、Generic Webhook Triggerを選んでください。次に述べるとおり、Multibranch側にはDocker構成と正面衝突する制約があります。

## Multibranch Pipelineを選ぶと効いてくるポート80の制約

GitBucketの公式Wikiは、Multibranch PipelineとOrganization Folderを使う前提条件として、Jenkinsが「ポート番号もコンテキストパスも付かないURL」でGitBucketへ到達できることを要求しています。具体的には次の縛りです。

- GitBucketをポート80で起動すること。ポートマッピング経由では動作しない
- `--prefix`（コンテキストパス）を付けないこと
- 管理画面のBase URLを空欄にせず、到達可能なURLを明示すること

Wikiはその理由を、GitHub系プラグインがGitBucketへ接続する際にURLからカスタムポートを取り除いてしまうこと、およびWebhookのURLと検出済みリポジトリURLを完全一致で照合することだと説明しています。APIエンドポイントには `http://gitbucket.example.com/api/v3` のような形を登録します。

Dockerでは `-p 8080:8080` のようなマッピングが当たり前なので、この条件は素直には満たせません。どうしてもMultibranchを使うなら、GITBUCKET\_PORTを80に変更したうえでホスト側も80で公開し、JenkinsからもブラウザからもDNS名で同じURLに到達できるよう揃えます。次はGitBucketサービスの差分だけを示したもので、`volumes` などの残りは前掲の定義をそのまま引き継いでください。

```
services:
  gitbucket:
    image: ghcr.io/gitbucket/gitbucket:4.46.1
    environment:
      GITBUCKET_PORT: "80"
    ports:
      - "80:80"
```

逆に、ポート80を他のサービスに使っている、社内DNSを整備していない、リポジトリが数個しかない、のいずれかに当てはまるなら、Multibranchは選ばないほうが安全です。URLの一致条件を満たせないまま導入すると、ジョブは作れてもWebhookが素通りし、原因の切り分けに時間を取られます。

## Generic Webhook Triggerでpush・プルリクエストを拾う設定

Generic Webhook Triggerは、任意のJSONを受け取ってJSONPathで変数に展開し、その値で発火条件を絞れるプラグインです。GitBucketのWebhookペイロードはGitHub互換なので、そのまま扱えます。

### GitBucket側のWebhook登録とContent typeの変更

対象リポジトリの Settings から Service Hooks を開き、Webhookを追加します。Composeで並べた構成なら、Payload URLはサービス名を使って次の形になります。

```
http://jenkins:8080/generic-webhook-trigger/invoke?token=gitbucket-hook
```

ここで必ず Content type を `application/json` に変更してください。GitBucketの新規Webhook作成画面は `application/x-www-form-urlencoded` が既定で、この形式ではリクエストボディが `payload=<URLエンコード済みJSON>` という1個のフォームパラメータとして送られます。Generic Webhook Trigger側はそれをフォームパラメータとして受け取り、`{"payload":["…"]}` という形に組み直してからJSONPathを評価します。そのため `$.ref` は解決できず、ジョブが永久に発火しません。`printPostContent` の出力にこの `payload` でくるまれたJSONが見えたら、Content typeの設定漏れが原因です。

トリガーするイベントは Push と Pull Request を選びます。GitBucketが送出できるイベントは `push` `pull_request` `issues` `issue_comment` `release` など19種類で、名称はGitHubのWebhookと同じです。

なお、Webhook設定画面にある token 欄は、Generic Webhook Triggerの認証トークンではありません。GitBucketはこの値をHMACの鍵として使い、`X-Hub-Signature` と `X-Hub-Signature-256` ヘッダーを付けて送ります。Generic Webhook Triggerはこの署名を検証しないため、プラグイン側のトークンはPayload URLのクエリ文字列で渡すことになります。GitBucketのWebhookフォームにカスタムヘッダーの入力欄が無い以上、ヘッダー渡しは選べません。ジョブ側の `tokenCredentialId` はJenkinsfileへのトークン直書きを避けるための機能で、URL上の露出そのものは消えない点に注意してください。露出を嫌うなら、Jenkinsを内部ネットワークに閉じる、あるいはリバースプロキシでアクセスログのクエリをマスクする対処になります。

### Jenkinsfileでの受け取りとブランチ絞り込み

宣言的パイプラインでは `triggers` ブロックに書きます。`regexpFilterText` と `regexpFilterExpression` の組み合わせで、対象ブランチ以外のpushを無視できます。

```
pipeline {
  agent any
  triggers {
    GenericTrigger(
      genericVariables: [
        [key: 'ref', value: '$.ref']
      ],
      causeString: 'Triggered by GitBucket push on $ref',
      token: 'gitbucket-hook',
      tokenCredentialId: '',
      printContributedVariables: true,
      printPostContent: true,
      regexpFilterText: '$ref',
      regexpFilterExpression: 'refs/heads/main'
    )
  }
  stages {
    stage('build') {
      steps {
        sh 'echo build from $ref'
      }
    }
  }
}
```

宣言的パイプラインの `triggers` はJenkinsfileを一度読み込んで初めてジョブに登録されます。作成直後に手動で1回ビルドしておかないとWebhookを送っても反応しません。初回は `printPostContent` を有効にしておくと、受信したJSONがコンソール出力にそのまま出ます。期待した変数が入らないときは、ここでペイロードの実物を見てJSONPathを直すのが最短です。

### プルリクエストのマージを起点にする場合の発火イベント

「プルリクエストがマージされたらデプロイしたい」という要件では、どのイベントが飛ぶかを正確に押さえておく必要があります。GitBucketの `MergeService.scala` を読むと、画面上のマージ操作は `push` と `pull_request`（action は `closed`）の両方を送出します。マージ方式がマージコミット・rebase・squashのいずれでも同じです。

ここで注意したいのは、`closed` がマージ以外でも飛ぶことです。プルリクエストをマージせずに閉じた場合も同じ `closed` になります。GitHubと同様、ペイロードには `merged` `merged_at` `merged_by` が含まれるので、マージだけを拾うなら `$.action` が `closed` かつ `$.pull_request.merged` が `true` という2条件で判定します。

| 操作             | 飛ぶイベント                          | ジョブ側の絞り込み                           |
| -------------- | ------------------------------- | ----------------------------------- |
| プルリクエスト作成      | `pull_request`（opened）          | `$.action` が opened                 |
| 対象ブランチへの追加push | `pull_request`（synchronize）     | `$.action` が synchronize            |
| マージせずに閉じる      | `pull_request`（closed）          | `$.pull_request.merged` が false     |
| 閉じたものを再オープン    | `pull_request`（reopened）        | `$.action` が reopened               |
| 画面上でマージ        | `push` と `pull_request`（closed） | `$.ref` がマージ先ブランチ、または merged が true |

マージ後のデプロイジョブは、pushの `$.ref` で受けても `pull_request` の merged で受けても組めます。ただし両方のイベントを1つのジョブで購読すると、1回のマージで二重に起動します。検証ジョブは `synchronize`、デプロイジョブは `refs/heads/main` のpush、というように購読するイベントを分けておくと事故が起きません。パイプラインの検証段にコンテナイメージの脆弱性スキャンを挟むなら、[Trivyの使い方とインストール手順](https://www.issoh.co.jp/tech/details/10398/)が参考になります。

## GitBucketのアクセストークンとJenkins資格情報

Jenkinsからプライベートリポジトリをcloneしたり、GitHub互換APIを叩いたりするには、パスワードではなくアクセストークンを使います。GitBucketでは画面右上のアカウントメニューから Applications を開くか、`/<ユーザー名>/_application` へ直接アクセスして発行します。

発行時の挙動で注意すべき点が1つあります。生成されたトークン文字列が画面に表示されるのは、発行直後の1回だけです。実装上もリダイレクト時のフラッシュメッセージで一度渡されるだけで、後から一覧画面を開いても用途を示すメモ（note）しか確認できません。控え忘れたら削除して再発行することになります。

Jenkins側では、この文字列をUsername with passwordの資格情報として登録するのが扱いやすい形です。ユーザー名にGitBucketのアカウント名、パスワード欄にトークンを入れます。HTTPでcloneする際はそのまま認証に使えます。Freestyleジョブやパイプラインからは資格情報IDで参照し、Jenkinsfileに直書きしないでください。

## H2からの移行とバックアップ

既定のデータベースは組み込みH2で、そのまま `/gitbucket` 配下に置かれます。利用者やリポジトリが増えてきたら、GitBucket 4.0以降で対応した外部データベースへ移せます。対応はMySQL 5.7以上とPostgreSQL 14以上で、設定ファイルは `GITBUCKET_HOME/database.conf`、Dockerでは `/gitbucket/database.conf` です。

接続設定は次の内容を `database.conf` として用意します。接続先ホストはComposeのサービス名です。

```
db {
  url = "jdbc:postgresql://db/gitbucket"
  user = "gitbucket_user"
  password = "YOUR_PASSWORD"
}
```

PostgreSQL側は、前掲のComposeに次の差分を足します。`gitbucket` サービスの `image` 以下や `jenkins` サービスは前掲の定義をそのまま引き継ぎ、トップレベルの `volumes` には `db_data` を追記してください。イメージタグをメジャー番号で固定しているのは、GitBucket本体のアップグレードとデータベースのメジャーアップグレードを別々のタイミングで実施できるようにするためです。

```
services:
  gitbucket:
    depends_on:
      - db
  db:
    image: postgres:17
    environment:
      POSTGRES_DB: gitbucket
      POSTGRES_USER: gitbucket_user
      POSTGRES_PASSWORD: YOUR_PASSWORD
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:
```

配置はホスト側で書いたファイルをコンテナへコピーする形が安全です。コピー先は名前付きボリュームの中なので、コンテナを作り直しても設定は残ります。

```
docker compose cp database.conf gitbucket:/gitbucket/database.conf
docker compose restart gitbucket
```

MariaDBは公式サポート対象外です。公式Wikiは10.2.1以上なら動く可能性があるとしつつ、10.4.4以降で不具合の報告があると注意を促しています。新規採用は避けてください。

H2に既存データがある場合は、管理画面のデータエクスポート・インポート機能で移行できます。バックアップで守るべき対象は、Gitリポジトリの実体とデータベースの状態の2つです。両者の時点がずれるとIssueやプルリクエストの参照関係が壊れます。公式Wikiのバックアップスクリプトも、全リポジトリのcloneと更新を挟んでからDBバックアップを取り、再度リポジトリを更新するという順序でこのずれを最小化しています。ただしWiki自身が「定期的に更新・保守されているものではなく、ミッションクリティカルな場面で頼るべきではない」と断っているため、そのまま本番運用に載せるのではなく、順序の考え方だけを取り込むのが現実的です。

アップグレードはwarの差し替えなので、Dockerではイメージタグの変更とコンテナの作り直しに相当します。`ghcr.io/gitbucket/gitbucket:4.46.1` のようにタグを固定しておけば、意図しないタイミングでのバージョン変動を避けられます。プラグインを追加している場合は、本体の更新に合わせて対応版へ入れ替える必要があります。

## つながらないときの切り分け

構築中に起きやすい症状と、最初に見るべき場所をまとめました。

| 症状                      | 確認する場所                     | 典型的な原因                                      |
| ----------------------- | -------------------------- | ------------------------------------------- |
| Webhookが届かない            | GitBucketのWebhook設定画面の配信履歴 | Payload URLにホスト側ポート（8081）を書いている             |
| Jenkinsが404を返す          | invokeのパスとtoken            | Generic Webhook Trigger未インストール、tokenの不一致    |
| 200が返るのにビルドが起動しない       | WebhookのContent type       | `x-www-form-urlencoded` のままでJSONPathが解決できない |
| 変数が空のまま発火しない            | printPostContentの出力        | regexpFilterExpressionがrefと一致していない          |
| Multibranchがリポジトリを検出しない | APIエンドポイントのURL             | ポート番号やprefixが付いている                          |
| SSHでcloneできない           | System SettingsのSSHアクセス    | 29418を公開しただけで機能を有効化していない                    |
| 再起動でデータが消える             | docker volume ls           | `/gitbucket` をボリューム化していない                   |

Webhook関連は、GitBucketの管理画面で送信結果のレスポンスコードを確認できます。ここが接続エラーならネットワークの問題、200以外のHTTPステータスならJenkins側の設定の問題、と切り分けの向きが決まります。

## よくある質問

### GitBucketとは何ですか

Scalaで開発されたオープンソースのGitプラットフォームです。リポジトリ管理、Issue、プルリクエスト、Wiki、アカウント・グループ管理、LDAP連携、プラグイン機構を備え、GitHub互換のAPIを提供します。1つのwarファイルで動くため導入が軽く、社内にGitサーバーを置きたい場合に選ばれます。

### docker run gitbucket/gitbucket で入るのはどのバージョンですか

Docker Hub側の `latest` は4.38.4で、2022年12月25日から更新されていません。最新版を使うなら `ghcr.io/gitbucket/gitbucket:4.46.1` を指定してください。

### JenkinsのGitBucket Pluginは今も使えますか

新規導入は避けてください。未修正の脆弱性を抱えたまま更新が止まっているためです。Webhook起点でジョブを動かす目的なら、Generic Webhook Triggerで代替できます。

### プルリクエストのマージだけを条件にビルドするにはどうしますか

マージ操作では `push` と `pull_request`（action は `closed`）の両方が飛びます。`closed` はマージせずに閉じた場合も同じ値なので、`$.action` が `closed` かつ `$.pull_request.merged` が `true` で判定するか、マージ先ブランチへのpush（`$.ref` が `refs/heads/main`）を条件にします。両方を同じジョブで購読すると二重起動します。

### Jenkinsで使うアクセストークンはどこで作りますか

GitBucket側で発行します。アカウントメニューのApplications、URLでは `/<ユーザー名>/_application` です。トークン文字列は発行直後にしか表示されないため、その場で控えてJenkinsの資格情報へ登録してください。

## 関連記事

- [docker-composeとは？複数コンテナをymlで定義し一括管理する仕組みを解説](https://www.issoh.co.jp/tech/details/13282/)
- [Dockerコンテナ起動コマンドの使い分け｜run・start・execと起動しない時の対処](https://www.issoh.co.jp/tech/details/3935/)
- [Jenkinsのインストール方法と初期設定手順に関するガイド](https://www.issoh.co.jp/tech/details/3957/)
- [Trivy（トリビー）とは｜使い方・インストール・読み方の入門ガイド【2026】](https://www.issoh.co.jp/tech/details/10398/)

---

出典: [GitBucketをDockerで構築する手順とJenkins連携の落とし穴](<https://www.issoh.co.jp/tech/details/3808/>)（株式会社一創）
