Task #244
Updated by Sanghoon Lee 5 days ago
Install the bitname package for Redmine and configure Redmine to use as an external issue tracker for GitLab
<pre>
[GitLab] ──→ External Issue Tracker Setting ──→ [Bitnami Redmine:3000]
│
[MariaDB Container]
</pre>
-----------------------
h1. Redmine Installation & GitLab Integration Procedures Guide
{{toc}}
---
h2. 1. System Architecture Overview
<pre>
┌─────────────────────────────────────────────────────────┐
│ GitLab │
│ │
│ Repository │ Issues │ Commit Messages │
└───────┬────────┴─────┬──────┴───────────┬───────────────┘
│ │ │
[1] Webhook │ │ [2] Integration │ [3] Commit message
push event triggered │ │ issue URL link │ XCP-#42 (refs #42)
│ │ │
v v │
┌───────────────────────────────┐ │
│ Redmine │ │
│ │ <───────┘
│ redmine_gitlab_hook plugin │ refs #42 → issue link
│ │ │
│ v │
│ git fetch --all -p │
│ (update mirror repository) │
│ │ │
│ v │
│ fetch_changesets │
│ (sync commits to DB) │
│ │
│ Issues │ Repository │ DB │
└───────────────────────────────┘
[1] Webhook : GitLab push → automatically syncs commits to Redmine repository
[2] Integration: GitLab Settings → Integrations → Redmine issue tracker URL linking
[3] Commit ref : "XCP-#42 (refs #42)" → creates issue links in both GitLab and Redmine
</pre>
|_.Component|_.Version / Details|
|Redmine|6.1 (redmine:6.1-trixie)|
|Database|MySQL 8.0|
|Plugin|redmine_gitlab_hook v4.0.0 (phlegx/redmine_gitlab_hook)|
|Host IP|192.168.1.102|
|Redmine Port|3000|
|GitLab URL|http://git.xela.com:8888|
---
h2. 2. Prerequisites
h3. 2-1. Verify Docker & Docker Compose Installation
<pre><code class="bash">
docker --version
docker compose version
</code></pre>
h3. 2-2. Create Required Directories
<pre><code class="bash">
mkdir -p /home/xela/redmine/redmine_data
mkdir -p /home/xela/redmine/mysql_data
mkdir -p /home/xela/redmine/redmine_repos
mkdir -p /home/xela/redmine/plugins
</code></pre>
h3. 2-3. Generate a GitLab Access Token
A token is required for authentication during git fetch operations.
# Go to GitLab → *User Settings → Access Tokens* (or project *Settings → Repository → Deploy tokens*)
# Select scope: @read_repository@
# Note down the generated *username* and *token* value
bq. *Deploy Token recommended*: Grants read-only access scoped to a specific project, and is not affected by account password changes.
---
h2. 3. Directory Structure & File Configuration
<pre>
/home/xela/redmine/
├── docker-compose.yml # Container orchestration
├── Dockerfile # Custom image with plugins included
├── SETUP.md # Korean setup guide
├── SETUP_EN.md # Markdown version
├── SETUP_EN.textile # This document (Redmine wiki format)
├── plugins/
│ └── redmine_gitlab_hook/ # GitLab webhook integration plugin
├── redmine_data/ # Persistent Redmine data (file attachments, etc.)
├── redmine_repos/ # Git mirror repositories (host path)
│ └── XCP.git/ # Example bare mirror repository
└── mysql_data/ # Persistent MySQL data
</pre>
h3. Key docker-compose.yml Settings
<pre><code class="yaml">
services:
redmine_db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: <change this>
MYSQL_USER: redmine
MYSQL_PASSWORD: <change this>
MYSQL_DATABASE: redmine
volumes:
- /home/xela/redmine/mysql_data:/var/lib/mysql
redmine:
build: . # Custom build from Dockerfile
ports:
- "192.168.1.102:3000:3000" # Bind to specific IP only
volumes:
- /home/xela/redmine/redmine_data:/usr/src/redmine/files
- /home/xela/redmine/redmine_repos:/var/lib/redmine/repositories:rw
</code></pre>
bq. *Volume mount note*: @redmine_repos@ (host) → @/var/lib/redmine/repositories@ (container).
Any changes to repository files on the host are immediately reflected inside the container.
h3. Dockerfile Overview
<pre><code class="dockerfile">
FROM redmine:6.1-trixie
# Install build tools
RUN apt-get install -y build-essential gcc make git default-libmysqlclient-dev
# Copy plugins and set ownership
COPY ./plugins /usr/src/redmine/plugins
RUN chown -R redmine:redmine /usr/src/redmine/plugins
# Install plugin gem dependencies
USER redmine
RUN bundle install
</code></pre>
bq. *When adding or updating plugins*, the image must be rebuilt: @docker compose build --no-cache@
---
h2. 4. Git Mirror Repository Setup
For each GitLab project to be integrated, create a local mirror repository on the Redmine server.
h3. 4-1. Create Mirror Clone
<pre><code class="bash">
# Format: git clone --mirror http://USER:TOKEN@GITLAB_HOST/NAMESPACE/REPO.git DEST.git
git clone --mirror \
http://invokelshn:TOKEN@git.xela.com:8888/software/XCP.git \
/home/xela/redmine/redmine_repos/XCP.git
</code></pre>
h3. 4-2. Verify Credentials Are Embedded in the Remote URL
<pre><code class="bash">
cat /home/xela/redmine/redmine_repos/XCP.git/config
</code></pre>
The @url@ field under @[remote "origin"]@ must include credentials in the format @http://USER:TOKEN@...:@
<pre><code class="ini">
[remote "origin"]
url = http://USERNAME:TOKEN@git.xela.com:8888/software/XCP.git
fetch = +refs/*:refs/*
mirror = true
</code></pre>
bq. *Important*: The Redmine container runs git fetch in a non-interactive environment — there is no TTY available to prompt for username/password.
Credentials must be embedded in the URL or configured via a credential store.
If the repository was cloned without credentials, update the URL:
<pre><code class="bash">
git --git-dir="/home/xela/redmine/redmine_repos/XCP.git" \
remote set-url origin \
"http://USERNAME:TOKEN@git.xela.com:8888/software/XCP.git"
</code></pre>
h3. 4-3. Verify fetch Works (Test from Host)
<pre><code class="bash">
git --git-dir="/home/xela/redmine/redmine_repos/XCP.git" fetch --all -p
</code></pre>
Expected output:
<pre>
From http://git.xela.com:8888/software/XCP
4acdd59..1caf25e main -> main
</pre>
---
h2. 5. Build & Run Docker Containers
h3. 5-1. Build Image and Start Containers
<pre><code class="bash">
cd /home/xela/redmine
docker compose build
docker compose up -d
</code></pre>
h3. 5-2. Verify Container Status
<pre><code class="bash">
docker compose ps
docker logs redmine --tail=30
</code></pre>
When Puma starts successfully:
<pre>
* Listening on http://0.0.0.0:3000
</pre>
h3. 5-3. Rebuild After Plugin Changes
<pre><code class="bash">
docker compose down
docker compose build --no-cache
docker compose up -d
</code></pre>
---
h2. 6. Redmine Initial Configuration (Web UI)
Redmine Web UI: @http://192.168.1.102:3000@ (or @http://redmine.xela.com:3000@)
Default admin credentials: @admin@ / @admin@ — *change immediately after first login*
h3. 6-1. Retrieve API Key
The plugin uses this key to authenticate incoming webhook requests.
*Administration → Settings → Repository* tab → Check *"Enable WS for repository management"* → Copy the API key
h3. 6-2. Register a Git Repository for a Redmine Project
For each project to be integrated:
# Go to the project → *Settings → Repositories* → *New repository*
# Fill in the settings:
|_.Field|_.Value|
|SCM|Git|
|Identifier|@xcp_mirror@ (must match the @repository_name@ parameter in the webhook URL)|
|Path to repository|@/var/lib/redmine/repositories/XCP.git@ (path inside the container)|
|Is default repository|Check|
3. Click *Create* and verify the repository is recognized correctly
h3. 6-3. Configure the redmine_gitlab_hook Plugin
*Administration → Plugins → Redmine GitLab Hook plugin → Configure*
|_.Setting|_.Value|_.Description|
|All Branches|Yes|Fetch all branches from all remotes|
|Prune|Yes|Remove stale remote-tracking references|
|Auto Create|Yes|Auto-create repository on first webhook (optional)|
|Fetch Updates|Yes|Run git fetch when webhook is received|
|Local Repositories Path|@/var/lib/redmine/repositories@|Required when Auto Create is enabled|
---
h2. 7. GitLab Webhook Configuration
h3. 7-1. Webhook URL Format
<pre>
http://redmine.xela.com:3000/gitlab_hook?project_id=REDMINE_PROJECT_ID&repository_name=REPO_IDENTIFIER&key=REDMINE_API_KEY
</pre>
|_.Parameter|_.Description|_.Example|
|@project_id@|Redmine project identifier|@fy26_xcp-project@|
|@repository_name@|Redmine repository identifier|@xcp_mirror@|
|@key@|Redmine API key (from section 6-1)|@u67PN12c2bDK5HyaTUdC@|
bq. *Important*: Use *@repository_name@*, not @repository_id@.
The plugin reads @params[:repository_name]@ — the @repository_id@ parameter is ignored entirely.
h3. 7-2. Register the Webhook in GitLab
# GitLab project → *Settings → Webhooks*
# Configure:
|_.Field|_.Value|
|URL|The webhook URL from above|
|Trigger|Check *Push events*|
|SSL verification|Configure as needed|
3. Click *Add webhook*
h3. 7-3. Example Configuration (XCP Project)
<pre>
http://redmine.xela.com:3000/gitlab_hook?project_id=fy26_xcp-project&repository_name=xcp_mirror&key=u67PN12c2bDK5HyaTUdC
</pre>
---
h2. 8. GitLab Integrations Setup (Issue Tracker Linking)
The *Settings → Integrations → Redmine* feature in GitLab allows issue numbers referenced in commit messages or merge requests to become clickable links that navigate directly to Redmine issues.
bq. *Webhook vs Integrations*
* *Webhook* (section 7): GitLab push event → automatically updates the Redmine repository mirror (commit sync)
* *Integrations* (this section): Links issue references in the GitLab UI to the Redmine issue tracker
h3. 8-1. Enable the Redmine Integration in GitLab
# GitLab project → *Settings → Integrations* → select *Redmine*
# Configure the following fields:
|_.Field|_.Value|_.Description|
|Active|Checked|Enable the integration|
|Project URL|@http://redmine.xela.com:3000/projects/fy26_xcp-project@|URL of the Redmine project|
|Issues URL|@http://redmine.xela.com:3000/issues/:id@|Issue URL pattern (keep @:id@ as-is)|
|New issue URL|@http://redmine.xela.com:3000/projects/fy26_xcp-project/issues/new@|URL to create a new issue|
3. Click *Save changes*
bq. The project identifier in *Project URL* and *New issue URL* must match the Redmine project identifier exactly.
Example: if the Redmine project identifier is @fy26_xcp-project@, use the same value in the URLs.
h3. 8-2. Referencing Issues in Commit Messages
Once the integration is active, use the following format to create issue links in both GitLab and Redmine simultaneously:
<pre><code class="bash">
# Format: "commit message GITLAB_PROJECT_ID-#issue_number (refs #issue_number)"
git commit -m "Fix sensor calibration logic XCP-#42 (refs #42)"
git push origin main
</code></pre>
|_.Reference format|_.Recognized by|_.Description|
|@XCP-#42@|GitLab|GitLab project ID (@XCP@) + issue number → links to GitLab issue|
|@refs #42@|Redmine|Redmine issue number → links to Redmine issue|
bq. *GitLab Project ID*: find it at GitLab project → Settings → General → *Project ID*
Replace @XCP@ with the actual project ID of your GitLab project.
In the GitLab commit detail page, @refs #42@ will appear as a clickable link that navigates to:
<pre>
http://redmine.xela.com:3000/issues/42
</pre>
h3. 8-3. Verify the Integration
* GitLab → project → *Settings → Integrations → Redmine* → click the *Test* button
* A successful connection shows a green checkmark with a success message
---
h2. 9. Integration Testing Procedure
h3. Step 1: Test git fetch Directly
Verify that git fetch works from inside the container.
<pre><code class="bash">
docker exec redmine git \
--git-dir="/var/lib/redmine/repositories/XCP.git" \
fetch --all -p
</code></pre>
* *Success*: Branch update information is printed
* *Failure*: See Troubleshooting section below
h3. Step 2: Manually Trigger Webhook from GitLab UI
# GitLab → project → *Settings → Webhooks*
# Click *Test → Push events* on the registered webhook
# Check the response:
** *@HTTP 200@*: Success
** *@HTTP 406@*: git fetch failed (authentication or path issue)
** *@HTTP 500@*: Redmine internal error (project/repository configuration issue)
h3. Step 3: Test Webhook via curl
<pre><code class="bash">
curl -X POST \
"http://redmine.xela.com:3000/gitlab_hook?project_id=fy26_xcp-project&repository_name=xcp_mirror&key=u67PN12c2bDK5HyaTUdC" \
-H "Content-Type: application/json" \
-d '{"object_kind":"push","ref":"refs/heads/main"}'
</code></pre>
*Expected response*: @OK@
h3. Step 4: Monitor Logs in Real Time
<pre><code class="bash">
# Terminal 1: watch logs
docker logs -f redmine
# Terminal 2: trigger webhook or push to GitLab
</code></pre>
Expected log on successful processing:
<pre>
Started POST "/gitlab_hook?..." for ...
Processing by GitlabHookController#index
Completed 200 OK
</pre>
h3. Step 5: Verify Commits Appear in Redmine UI
# Redmine → the project → *Repository* tab
# Confirm that commits pushed to GitLab are now visible
h3. Step 6: End-to-End Test
<pre><code class="bash">
# 1. Create a test commit (from a local development environment)
git commit --allow-empty -m "test: redmine webhook integration"
git push origin main
# 2. After a few seconds, check Redmine
# Project → Repository → verify the commit appears in the list
</code></pre>
---
h2. 10. Troubleshooting
h3. Error 1: HTTP 500 — "has no repository or repository not found"
<pre>
TypeError: Project 'XXX' has no repository or repository not found with identifier 'yyy'
</pre>
*Cause A*: Webhook URL uses @repository_id@ instead of @repository_name@
* *Fix*: Replace @repository_id=...@ with @repository_name=...@ in the webhook URL
*Cause B*: No repository registered for the Redmine project
* *Fix*: Register a Git repository under project Settings → Repositories (see section 6-2)
*Cause C*: Mismatch between @repository_name@ parameter and the Redmine repository identifier
* *Fix*: Ensure the Redmine repository identifier exactly matches the @repository_name@ value in the webhook URL
---
h3. Error 2: HTTP 406 — "Git command failed"
<pre>
GitLabHook: Command 'git --git-dir="..." fetch --all -p' didn't exit properly.
fatal: could not read Username for 'http://...': No such device or address
</pre>
*Cause*: The mirror repository's remote URL does not contain credentials
*Fix*:
<pre><code class="bash">
git --git-dir="/home/xela/redmine/redmine_repos/XCP.git" \
remote set-url origin \
"http://USERNAME:TOKEN@git.xela.com:8888/software/XCP.git"
</code></pre>
---
h3. Error 3: Cannot Reach GitLab Host from Inside the Container
<pre>
fatal: unable to access 'http://git.xela.com:8888/...': Could not resolve host
</pre>
*Check*:
<pre><code class="bash">
docker exec redmine curl -I http://git.xela.com:8888
</code></pre>
*Fix*: Add @extra_hosts@ to @docker-compose.yml@ or verify DNS configuration on the host.
---
h3. How to Check Logs
<pre><code class="bash">
# Container logs (last 100 lines)
docker logs --tail=100 redmine
# Follow logs in real time
docker logs -f redmine
# Redmine production.log (inside container)
docker exec redmine tail -100 /usr/src/redmine/log/production.log
</code></pre>
---
h2. 11. Maintenance
h3. Adding a New GitLab Project Integration
# Create a mirror repository (see section 4)
# Register the repository in the Redmine project (see section 6-2)
# Add a webhook in GitLab (see section 7)
# Run through the test procedure (see section 9)
h3. Restart Containers
<pre><code class="bash">
cd /home/xela/redmine
docker compose restart
</code></pre>
h3. Update the Plugin
<pre><code class="bash">
cd /home/xela/redmine/plugins/redmine_gitlab_hook
git pull
cd /home/xela/redmine
docker compose down
docker compose build --no-cache
docker compose up -d
</code></pre>
h3. Backup
<pre><code class="bash">
# MySQL database
docker exec redmine_db mysqldump -u redmine -pPASSWORD redmine > backup_$(date +%Y%m%d).sql
# Persistent Redmine data (file attachments, etc.)
tar -czf redmine_data_$(date +%Y%m%d).tar.gz /home/xela/redmine/redmine_data
# Git mirror repositories
tar -czf redmine_repos_$(date +%Y%m%d).tar.gz /home/xela/redmine/redmine_repos
</code></pre>