Task #244
Updated by Sanghoon Lee 4 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 Guide — Work Procedure
{{toc}} *Document Version:* 1.0
*Date:* 2026-03-24
*Environment:* Debian 13 (Trixie), Docker Compose v5.x, GitLab (Docker), Redmine (Docker)
---
h2. 1. System Architecture Environment Overview
|_.Item|_.Details|
|Host OS|Debian 13 (Trixie)|
|Docker Compose|v5.0.2|
|GitLab URL|http://git.xela.com:8888|
|GitLab Host IP|192.168.1.102|
|Redmine URL|http://redmine.xela.com:3000|
|Redmine Host IP|192.168.1.102 (same host)|
|Redmine Image|redmine:6.1-trixie (Official Docker Image)|
|Database Image|mysql:8.0|
---
h2. 2. Architecture
<pre>
┌─────────────────────────────────────────────────────────┐
Client Browser
│
├── http://git.xela.com:8888 → GitLab │
│ │
│ Repository │ Issues │ Commit Messages │
└───────┬────────┴─────┬──────┴───────────┬───────────────┘
│ │ │
[1] Webhook │ │ [2] Integration Container (existing)
└── http://redmine.xela.com:3000 │ [3] Commit message
push event triggered → │ │ issue URL link │ XCP-#42 (refs #42)
│ │ │
v v │
┌───────────────────────────────┐ │
│ Redmine Container (new)
│ │
│ │ <───────┘
│ 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
MySQL Container
(redmine_db, internal only)
</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. 3. Prerequisites
* Docker is already installed and running
* GitLab is already running as a Docker container on port 8888
* DNS or @/etc/hosts@ entries are configured for both domains
h3. 2-1. 3.1 Verify Docker & and Docker Compose Installation
<pre><code class="bash"> <pre>
docker --version
docker compose version
</code></pre> </pre>
---
h2. 4. Redmine Installation
h3. 2-2. 4.1 Create Required Data Directories
<pre><code class="bash"> <pre>
sudo mkdir -p /home/xela/redmine/redmine_data /opt/redmine/redmine_data
sudo mkdir -p /home/xela/redmine/mysql_data /opt/redmine/mysql_data
mkdir -p /home/xela/redmine/redmine_repos
mkdir -p /home/xela/redmine/plugins
</code></pre> </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 & 4.2 Create Docker Compose File Configuration
<pre>
/home/xela/redmine/ sudo nano /opt/redmine/docker-compose.yml
├── 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 Paste the following content:
<pre><code class="yaml"> <pre>
services:
redmine_db:
image: mysql:8.0
container_name: redmine_db
restart: always
environment:
MYSQL_ROOT_PASSWORD: <change this> root_secret # Change this
MYSQL_USER: redmine
MYSQL_PASSWORD: <change this> redmine_secret # Change this
MYSQL_DATABASE: redmine
volumes:
- /home/xela/redmine/mysql_data:/var/lib/mysql /opt/redmine/mysql_data:/var/lib/mysql
networks:
- redmine_net
redmine:
build: . # Custom build from Dockerfile image: redmine:6.1-trixie
container_name: redmine
restart: always
depends_on:
- redmine_db
ports:
- "192.168.1.102:3000:3000"
environment:
REDMINE_DB_MYSQL: redmine_db
REDMINE_DB_USERNAME: redmine
REDMINE_DB_PASSWORD: redmine_secret # Bind to specific IP only Same as above
REDMINE_DB_DATABASE: redmine
REDMINE_SECRET_KEY_BASE: change_this_to_long_random_string # Change this
volumes:
- /home/xela/redmine/redmine_data:/usr/src/redmine/files /opt/redmine/redmine_data:/usr/src/redmine/files
networks:
- /home/xela/redmine/redmine_repos:/var/lib/redmine/repositories:rw
</code></pre> redmine_net
bq. *Volume mount note*: @redmine_repos@ (host) → @/var/lib/redmine/repositories@ (container). networks:
redmine_net:
driver: bridge
Any changes to repository files on the host are immediately reflected inside the container. </pre>
h3. Dockerfile Overview 4.3 Start Containers
<pre><code class="dockerfile"> <pre>
FROM redmine:6.1-trixie
# Install build tools cd /opt/redmine
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 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"> up -d
# 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> </pre>
h3. 4-2. 4.4 Verify Credentials Are Embedded in the Remote URL Startup
<pre><code class="bash"> <pre>
cat /home/xela/redmine/redmine_repos/XCP.git/config docker compose logs -f redmine
</code></pre> </pre>
The @url@ field under @[remote "origin"]@ must include credentials in Wait until the format @http://USER:TOKEN@...:@
<pre><code class="ini"> following message appears:
[remote "origin"]
url = http://USERNAME:TOKEN@git.xela.com:8888/software/XCP.git
fetch = +refs/*:refs/*
mirror = true <pre>
</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. * Listening on http://0.0.0.0:3000
Credentials must be embedded in the URL or configured via a credential store. </pre>
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. 4.5 Verify fetch Works (Test from Host) Access
<pre><code class="bash"> <pre>
git --git-dir="/home/xela/redmine/redmine_repos/XCP.git" fetch --all -p curl -I http://192.168.1.102:3000
</code></pre> </pre>
Expected output: response: @HTTP/1.1 200 OK@
<pre>
From http://git.xela.com:8888/software/XCP
4acdd59..1caf25e main -> main
</pre>
---
h2. 5. Build & Run Docker Containers DNS / hosts Configuration
h3. 5-1. Build Image and Start Containers If an internal DNS server is not available, add the following entry to the @/etc/hosts@ file on *each client PC* that needs access.
<pre><code class="bash"> *Linux / Mac:*
cd /home/xela/redmine <pre>
docker compose build sudo nano /etc/hosts
docker compose up -d # Add the following line:
</code></pre> 192.168.1.102 redmine.xela.com
</pre>
h3. 5-2. Verify Container Status
<pre><code class="bash"> *Windows* (@C:\Windows\System32\drivers\etc\hosts@):
docker compose ps
docker logs redmine --tail=30
</code></pre>
When Puma starts successfully:
<pre>
* Listening on http://0.0.0.0:3000 192.168.1.102 redmine.xela.com
</pre>
h3. 5-3. Rebuild After Plugin Changes {{warning(Note: @nslookup@ queries DNS directly and ignores @/etc/hosts@. Use @ping@ or @curl@ to verify that the hostname resolves correctly to @192.168.1.102@.)}}
<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)
h3. 6.1 Log in to Redmine Web UI: @http://192.168.1.102:3000@ (or @http://redmine.xela.com:3000@)
Open a browser and navigate to:
<pre>
http://redmine.xela.com:3000
</pre>
Default admin credentials:
* Username: @admin@ /
* Password: @admin@ — *change immediately after
{{warning(You will be prompted to change the password on first login* login.)}}
h3. 6-1. Retrieve 6.2 Enable REST API Key
The plugin uses this key to authenticate incoming webhook requests.
*Administration <pre>
Administration → Settings → Repository* API tab
→ Check *"Enable WS for repository management"* "Enable REST web service"
→ Copy the API key Save
</pre>
h3. 6-2. Register 6.3 Create a Git Repository for a Redmine Project
For each project to be integrated:
# Go to the project <pre>
Projects → *Settings New Project
→ Repositories* Name: (your project name)
→ *New repository* Identifier: (lowercase letters, numbers, hyphens only — e.g., xcp)
# Fill in the settings: → Save
</pre>
|_.Field|_.Value|
|SCM|Git|
|Identifier|@xcp_mirror@ (must match {{warning(Note the @repository_name@ parameter *Identifier* — it will be used in the webhook URL)|
|Path to repository|@/var/lib/redmine/repositories/XCP.git@ (path inside the container)|
|Is default repository|Check| GitLab integration URL settings.)}}
3. Click *Create* and verify the repository is recognized correctly
h3. 6-3. Configure the redmine_gitlab_hook Plugin 6.4 Add User
*Administration → Plugins → Redmine GitLab Hook plugin → Configure* !clipboard-202603241708-aqn9v.png!
|_.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 Integration Setup
h3. 7-1. Webhook URL Format 7.1 Allow Local Network Requests in GitLab
<pre> GitLab blocks requests to internal network addresses by default (SSRF protection).
http://redmine.xela.com:3000/gitlab_hook?project_id=REDMINE_PROJECT_ID&repository_name=REPO_IDENTIFIER&key=REDMINE_API_KEY
</pre> This must be explicitly allowed.
|_.Parameter|_.Description|_.Example| <pre>
|@project_id@|Redmine project identifier|@fy26_xcp-project@| GitLab → Admin Area → Settings → Network
|@repository_name@|Redmine repository identifier|@xcp_mirror@|
|@key@|Redmine API key (from → Outbound requests section 6-1)|@u67PN12c2bDK5HyaTUdC@|
bq. *Important*: Use *@repository_name@*, not @repository_id@.
The plugin reads @params[:repository_name]@ — → Check "Allow requests to the @repository_id@ parameter is ignored entirely.
h3. 7-2. Register local network from webhooks and integrations"
→ In the Webhook in GitLab
# GitLab project allowlist input field, add:
192.168.1.102
redmine.xela.com
→ *Settings → Webhooks* Save changes
# Configure: </pre>
|_.Field|_.Value| Direct URL:
|URL|The webhook URL from above| <pre>
|Trigger|Check *Push events*| http://git.xela.com:8888/admin/application_settings/network
|SSL verification|Configure as needed| </pre>
3. Click *Add webhook*
h3. 7-3. Example Configuration (XCP Project) 7.2 Configure Redmine Integration (Admin Template)
<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 → Admin Area → 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) Project URL: http://redmine.xela.com:3000/projects/:issues_tracker_id
* *Integrations* (this section): Links issue references in → Issue URL: http://redmine.xela.com:3000/issues/:id
→ New Issue URL: http://redmine.xela.com:3000/projects/:issues_tracker_id/issues/new
→ Save changes
</pre>
{{note(This sets the GitLab UI default template. Individual projects still need to the Redmine issue tracker be activated separately.)}}
h3. 8-1. Enable the 7.3 Configure Redmine Integration in per Project
For each GitLab project that should use Redmine:
# <pre>
GitLab project Project → *Settings Settings → Integrations* Integrations → select *Redmine* Redmine
# Configure the following fields:
|_.Field|_.Value|_.Description| → Active: Checked
|Active|Checked|Enable the integration| → Project URL: http://redmine.xela.com:3000/projects/<redmine-project-identifier>
|Project URL|@http://redmine.xela.com:3000/projects/fy26_xcp-project@|URL of the Redmine project| → Issue URL: http://redmine.xela.com:3000/issues/:id
|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/<redmine-project-identifier>/issues/new
|New issue URL|@http://redmine.xela.com:3000/projects/fy26_xcp-project/issues/new@|URL to create a new issue| → Save changes
</pre>
3. Click *Save changes*
bq. The project identifier in *Project URL* and *New issue URL* must match {{note(Replace @<redmine-project-identifier>@ with the actual Redmine project identifier exactly.
Example: if the Redmine project identifier is @fy26_xcp-project@, use the same value in the URLs. (e.g., @xcp@).)}}
h3. 8-2. Referencing Issues in Commit Messages
Once the integration is active, use the following format to create issue links in both 7.4 Disable GitLab and Redmine simultaneously: Internal Issue Tracker per Project
<pre><code class="bash"> <pre>
# 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 Visibility, project ID of your GitLab project.
In the GitLab commit detail page, @refs #42@ will appear as a clickable link that navigates to:
<pre> features, permissions (expand)
http://redmine.xela.com:3000/issues/42
</pre>
h3. 8-3. Verify the Integration
* GitLab → project Issues (or Work items) toggle → *Settings OFF
→ Integrations → Redmine* → click the *Test* button Save changes
* A successful connection shows a green checkmark with a success message </pre>
---
h2. 9. Integration 8. Verification & Testing Procedure
h3. Step 1: Test git fetch Directly
8.1 Verify that git fetch works from inside the container. Redmine Menu in GitLab
<pre><code class="bash"> <pre>
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 → project Left sidebar
→ *Settings Plan → Webhooks* Redmine
# 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 \ Confirm that it redirects to:
"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"}' http://redmine.xela.com:3000/projects/<redmine-project-identifier>
</code></pre> </pre>
*Expected response*: @OK@
h3. Step 4: Monitor Logs 8.2 Verify Issue Link in Real Time Commit Message
<pre><code class="bash"> *Step 1.* Create a test issue in Redmine:
# 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 ... Redmine → Project → Issues → New Issue
Processing by GitlabHookController#index → Subject: "Test Issue"
Completed 200 OK → Submit
</pre>
h3. Step 5: Verify Commits Appear in Redmine UI
# Redmine → Note the project → *Repository* tab issue number (e.g., #1)
# Confirm that commits pushed to GitLab are now visible </pre>
h3. Step 6: End-to-End Test
<pre><code class="bash">
# 1. *Step 2.* Create a test commit (from a local development environment) with the Redmine issue URL in the message:
<pre>
echo "redmine integration test" > test.txt
git add test.txt
git commit --allow-empty -m "test: redmine webhook integration" "Integration test - http://redmine.xela.com:3000/issues/1"
git push origin main <branch-name>
</pre>
# 2. After a few seconds, check Redmine *Step 3.* Verify the link in GitLab:
# <pre>
GitLab Project → Repository Code → verify Commits
→ Click on the test commit appears
→ Confirm that the Redmine URL in the list commit message is a clickable link
</code></pre> → Click the link and confirm it opens:
http://redmine.xela.com:3000/issues/1
</pre>
---
h2. 10. Troubleshooting
h3. Error 1: HTTP 500 — "has no repository or repository not found" 8.3 Verification Checklist
<pre> |_.#|_.Test Item|_.Expected Result|_.Pass/Fail|
TypeError: Project 'XXX' has no repository or repository not found with identifier 'yyy' |1|Access http://redmine.xela.com:3000|Redmine login page displayed|[ ]|
</pre>
*Cause A*: Webhook URL uses @repository_id@ instead of @repository_name@ |2|GitLab → Plan → Redmine menu visible|Menu item appears in sidebar|[ ]|
* *Fix*: Replace @repository_id=...@ with @repository_name=...@ in the webhook URL
*Cause B*: No repository registered for the |3|Click Plan → Redmine|Redirects to Redmine project page|[ ]|
* *Fix*: Register a Git repository under project Settings → Repositories (see section 6-2)
*Cause C*: Mismatch between @repository_name@ parameter and the |4|Commit message with Redmine repository identifier URL|URL appears as clickable link|[ ]|
* *Fix*: Ensure the |5|Click Redmine repository identifier exactly matches the @repository_name@ value URL in the webhook URL commit|Opens correct Redmine issue page|[ ]|
|6|GitLab internal Issues menu|Not visible (disabled)|[ ]|
---
h3. Error 2: HTTP 406 — "Git command failed" h2. 9. Container Management
*Start Redmine*
<pre>
GitLabHook: Command 'git --git-dir="..." fetch --all -p' didn't exit properly. cd /opt/redmine
fatal: could not read Username for 'http://...': No such device or address docker compose up -d
</pre>
*Cause*: The mirror repository's remote URL does not contain credentials
*Fix*:
<pre><code class="bash"> *Stop Redmine*
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 cd /opt/redmine
</pre>
*Check*:
<pre><code class="bash">
docker exec redmine curl -I http://git.xela.com:8888 compose down
</code></pre> </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"> *View Logs*
# Container logs (last 100 lines) <pre>
docker compose logs --tail=100 -f redmine
# Follow Redmine logs in real time
docker compose logs -f redmine
redmine_db # Redmine production.log (inside container) MySQL logs
docker exec redmine tail -100 /usr/src/redmine/log/production.log
</code></pre> </pre>
---
h2. 11. Maintenance
h3. Adding a New GitLab Project Integration
# Create a mirror repository (see section 4)
# Register the repository in the *Restart Redmine project (see section 6-2) only*
# Add a webhook in GitLab (see section 7) <pre>
# Run through the test procedure (see section 9)
h3. Restart Containers
<pre><code class="bash">
cd /home/xela/redmine
docker compose restart redmine
</code></pre> </pre>
h3. Update the Plugin ---
<pre><code class="bash">
cd /home/xela/redmine/plugins/redmine_gitlab_hook
git pull h2. 10. Notes & Limitations
cd /home/xela/redmine |_.Item|_.Details|
docker compose down |Issue sync direction|One-way only: GitLab commit messages link to Redmine issues|
docker compose build --no-cache |Two-way sync|Not supported natively. Requires additional plugins|
docker compose up -d |Commit message format|Include full Redmine issue URL (e.g., @http://redmine.xela.com:3000/issues/1@)|
</code></pre>
h3. Backup
<pre><code class="bash"> |@#id@ shorthand|Does not link to Redmine — links to GitLab internal issues instead|
# MySQL database |Protected branches|Direct push to @main@ may be blocked — use feature branches|
docker exec redmine_db mysqldump -u redmine -pPASSWORD redmine > backup_$(date +%Y%m%d).sql |Per-project setup|Redmine integration and Issues toggle must be configured per GitLab project|
# 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>
_End of Document_