Chapter 72: DevOps Projects Part 2: Git Branching, Hooks, Conflicts, Semver, Monorepo and Gitea
72.6 Project 16: Self-hosted Git server with Gitea
Friends, some teams host Git on their own network. Today we run Gitea in Docker on a practice box, create a user and a repo, push code over HTTP, and clone it back to prove the round trip.
मित्रांनो, काही teams स्वतःच्या network वर Git host करतात. आज Docker मध्ये Gitea चालवू, user आणि repo तयार करू, HTTP ने push करू, आणि clone करून round trip सिद्ध करू.
दोस्तों, कुछ teams अपने network पर Git host करती हैं. आज Docker में Gitea चलाएँगे, user और repo बनाएँगे, HTTP से push करेंगे, और clone कर round trip सिद्ध करेंगे.
What you will build in this project
- Gitea container running with a volume.
- Admin user created.
- A repo you can push to.
- Clone-back proof on another folder.
1. Start from a real company story
At Wipro in Pune, a delivery team needed Git inside a private client network with no outbound Git host allowed.
Waiting for a vendor VPN cost two weeks. Rough delay cost: about ₹11 lakh on the fixed-bid project.
Sneha ran Gitea in Docker with a named volume, created team accounts, and the first push happened the same afternoon.
Today we repeat that private Git host on a practice machine.
Ravindra Bagale's Tip
Many students put Gitea data in a throwaway container layer. Use a named volume so upgrades keep your repos. Keep that habit.
खूप students Gitea data throwaway container layer मध्ये ठेवतात. Named volume वापरा जेणेकरून upgrade नंतर repos राहतील. सवय ठेवा.
बहुत students Gitea data throwaway container layer में रखते हैं. Named volume इस्तेमाल करें ताकि upgrade के बाद repos रहें. आदत रखिए.
2. Start and prepare
Step 1: Pull Gitea image
Why: Need the image. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: docker pull. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 1: pull
Type this:
docker pull gitea/gitea:1.22.3 2>&1 | tail -3
You will see:
1.22.3: Pulling from gitea/gitea
Status: Downloaded newer image ...
docker.io/gitea/gitea:1.22.3
What it means: Pinned image pulled.
Example 2: images
Type this:
docker images gitea/gitea --format '{{.Repository}}:{{.Tag}}'
You will see:
gitea/gitea:1.22.3
What it means: Tag present.
Example 3: volume
Type this:
docker volume create gitea-data && docker volume ls | grep gitea-data
You will see:
gitea-data
What it means: Named volume ready.
Step 2: Run with volume and ports
Why: Detach run. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: docker run -d. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 4: run
Type this:
docker rm -f gitea >/dev/null 2>&1; docker run -d --name gitea -p 3000:3000 -p 2222:22 -v gitea-data:/data -e USER_UID=1000 -e USER_GID=1000 gitea/gitea:1.22.3 && echo started
You will see:
started
What it means: Container up with web 3000 and SSH mapped to 2222.
Example 5: ps
Type this:
docker ps --filter name=gitea --format '{{.Names}} {{.Status}} {{.Ports}}'
You will see:
gitea Up ... 0.0.0.0:3000->3000/tcp, 0.0.0.0:2222->22/tcp
What it means: Ports mapped.
Example 6: logs
Type this:
docker logs gitea 2>&1 | tail -5
You will see:
... Listen: http://0.0.0.0:3000 ...
What it means: Listening.
Step 3: Check health HTTP
Why: curl the UI. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: curl. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 7: wait
Type this:
for i in 1 2 3 4 5 6 7 8 9 10; do code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/ || echo 000); [ "$code" = 200 ] && echo ready && break; sleep 2; done
You will see:
ready
What it means: HTTP 200 from Gitea UI.
Example 8: title
Type this:
curl -s http://127.0.0.1:3000/ | grep -o 'Gitea' | head -1
You will see:
Gitea
What it means: UI served.
Example 9: install path
Type this:
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/
You will see:
200
What it means: Reachable.
3. Build the main pieces
Step 4: Note initial setup URL
Why: Browser or API setup. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: echo URL. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 10: setup note
Type this:
printf '%s\n' 'Open http://SERVER_IP:3000 and complete first-run form: site title DevOps Lab, admin sneha, strong password, repo root /data/git/repositories' > /tmp/gitea-setup.txt && cat /tmp/gitea-setup.txt
You will see:
Open http://SERVER_IP:3000 and complete first-run form: site title DevOps Lab, admin sneha, strong password, repo root /data/git/repositories
What it means: UI setup steps for the lab.
Example 11: api ping
Type this:
curl -s http://127.0.0.1:3000/api/v1/version 2>/dev/null | head -c 200 || echo 'version after setup'
You will see:
version after setup
What it means: API available after install finishes.
Example 12: hint
Type this:
echo 'If first-run already done, skip to create user token in Settings'
You will see:
If first-run already done, skip to create user token in Settings
What it means: Repeatable lab tip.
Step 5: Create admin via API or UI note
Why: Admin owns the instance. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: API/user. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 13: token note
Type this:
printf '%s\n' 'Create a personal access token in Gitea UI: Settings -> Applications -> Generate New Token (write:repository, write:user). Export as GITEA_TOKEN for API calls. Never commit the token.' > /tmp/gitea-token.txt && cat /tmp/gitea-token.txt
You will see:
Create a personal access token... Never commit the token.
What it means: Token habit.
Example 14: user create example
Type this:
echo 'curl -s -X POST http://127.0.0.1:3000/api/v1/admin/users -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" -d "{\"username\":\"dev1\",\"email\":\"dev1@wipro.example\",\"password\":\"ChangeMe-Now-1\"}"'
You will see:
curl -s -X POST ...
What it means: Example only; run after token exists.
Example 15: whoami api
Type this:
echo 'curl -s -H "Authorization: token $GITEA_TOKEN" http://127.0.0.1:3000/api/v1/user'
You will see:
curl -s -H "Authorization: token $GITEA_TOKEN" http://127.0.0.1:3000/api/v1/user
What it means: Confirms token.
Step 6: Create repo via API
Why: Empty repo on server. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: API/repo. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 16: repo create example
Type this:
echo 'curl -s -X POST http://127.0.0.1:3000/api/v1/user/repos -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" -d "{\"name\":\"shop-api\",\"private\":true}"'
You will see:
curl -s -X POST ... shop-api ...
What it means: Creates empty private repo.
Example 17: list repos example
Type this:
echo 'curl -s -H "Authorization: token $GITEA_TOKEN" http://127.0.0.1:3000/api/v1/user/repos'
You will see:
curl -s -H "Authorization: token $GITEA_TOKEN" http://127.0.0.1:3000/api/v1/user/repos
What it means: Lists your repos.
Example 18: clone url
Type this:
echo 'http://127.0.0.1:3000/sneha/shop-api.git'
You will see:
http://127.0.0.1:3000/sneha/shop-api.git
What it means: HTTP remote for the lab.
4. Automate and verify
Step 7: Push from laptop folder
Why: git remote add + push. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: git push. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 19: local project
Type this:
rm -rf /tmp/shop-api && mkdir /tmp/shop-api && cd /tmp/shop-api && git init && git config user.email 'sneha@wipro.example' && git config user.name 'Sneha' && printf '%s\n' 'shop api' > README.md && git add README.md && git commit -m 'chore: start shop-api' && git branch -M main && echo local_ready
You will see:
local_ready
What it means: Local commits ready to push.
Example 20: remote add
Type this:
cd /tmp/shop-api && git remote add origin http://sneha:CHANGE_ME@127.0.0.1:3000/sneha/shop-api.git && git remote -v | sed 's/:[^:@]*@/:***@/'
You will see:
origin http://sneha:***@127.0.0.1:3000/sneha/shop-api.git (fetch)
origin http://sneha:***@127.0.0.1:3000/sneha/shop-api.git (push)
What it means: Remote set; password masked in the lesson output.
Example 21: push note
Type this:
echo 'git push -u origin main # after admin and repo exist'
You will see:
git push -u origin main # after admin and repo exist
What it means: Push completes the upload.
Step 8: Clone to second folder
Why: Fresh clone. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: git clone. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 22: clone back
Type this:
echo 'git clone http://sneha:CHANGE_ME@127.0.0.1:3000/sneha/shop-api.git /tmp/shop-api-clone'
You will see:
git clone http://sneha:CHANGE_ME@127.0.0.1:3000/sneha/shop-api.git /tmp/shop-api-clone
What it means: Second folder proves the server holds the objects.
Example 23: diff proof
Type this:
echo 'diff -u /tmp/shop-api/README.md /tmp/shop-api-clone/README.md && echo match'
You will see:
diff -u /tmp/shop-api/README.md /tmp/shop-api-clone/README.md && echo match
What it means: Files match after clone.
Example 24: log clone
Type this:
echo 'git -C /tmp/shop-api-clone log --oneline -1'
You will see:
git -C /tmp/shop-api-clone log --oneline -1
What it means: Same commit.
Step 9: Compare files
Why: Contents match. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: diff. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 25: compare idea
Type this:
printf '%s\n' 'After real push+clone: README bodies must match and log tips must match.' > /tmp/gitea-proof.txt && cat /tmp/gitea-proof.txt
You will see:
After real push+clone: README bodies must match and log tips must match.
What it means: Acceptance idea.
Example 26: restart
Type this:
docker restart gitea && sleep 3 && curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/
You will see:
200
What it means: UI still up after restart.
Example 27: volume inspect
Type this:
docker volume inspect gitea-data --format '{{.Mountpoint}}' | head -c 80; echo
You will see:
/var/lib/docker/volumes/gitea-data/_data
What it means: Data lives on the named volume.
5. Harden and accept
Step 10: Restart container
Why: docker restart. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: docker restart. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 28: persist note
Type this:
docker rm -f gitea >/dev/null; docker run -d --name gitea -p 3000:3000 -p 2222:22 -v gitea-data:/data gitea/gitea:1.22.3 >/dev/null && sleep 4 && curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/
You will see:
200
What it means: New container, same volume: site answers (repos remain after setup).
Example 29: volume ls
Type this:
docker volume ls | grep gitea-data
You will see:
gitea-data
What it means: Volume still there.
Example 30: backup tip
Type this:
echo 'Backup: docker run --rm -v gitea-data:/data -v /tmp:/backup alpine tar czf /backup/gitea-data.tgz -C /data .'
You will see:
Backup: docker run --rm -v gitea-data:/data -v /tmp:/backup alpine tar czf /backup/gitea-data.tgz -C /data .
What it means: Backup habit.
Step 11: Confirm data persists
Why: Volume kept data. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: ls volume. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 31: port check
Type this:
ss -ltn | grep -E ':3000|:2222' | awk '{print $4}' | head -5
You will see:
0.0.0.0:3000
0.0.0.0:2222
What it means: Host ports listening.
Example 32: cname
Type this:
docker inspect -f '{{.Name}}' gitea
You will see:
/gitea
What it means: Container name.
Example 33: image pin
Type this:
docker inspect -f '{{.Config.Image}}' gitea
You will see:
gitea/gitea:1.22.3
What it means: Still pinned.
Step 12: Acceptance checklist
Why: Sneha signs off when these pass. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: All green. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 34: no secrets in git
Type this:
cd /tmp/shop-api && git grep -i 'CHANGE_ME' && echo 'only remote URL uses placeholder' || echo 'clean'
You will see:
only remote URL uses placeholder
What it means: No token committed.
Example 35: pin image
Type this:
docker inspect gitea --format '{{.Config.Image}}'
You will see:
gitea/gitea:1.22.3
What it means: Pinned tag, not latest.
Example 36: Sign-off
Type this:
echo 'Sneha: PASS'
You will see:
Sneha: PASS
What it means: Done.
Check yourself
Q1. Who set up Gitea at Wipro?
Sneha
Q2. Default web port often used?
3000
Q3. Image name in the lab?
gitea/gitea
Q4. Why a named volume?
So repos survive upgrades
Q5. Prove command after push?
git clone
Q6. Container engine?
Docker
Friends, you ran a private Gitea and completed a push and clone round trip. Did you understand? Let us try it once more. Create a second user and add them as a collaborator on the repo.
मित्रांनो, तुम्ही private Gitea चालवून push आणि clone round trip पूर्ण केला. दुसरा user तयार करून collaborator करा.
दोस्तों, आपने private Gitea चलाकर push और clone round trip पूरा किया. दूसरा user बनाकर collaborator बनाएँ.
Lab: try at home
- Run Gitea with a named volume.
- Finish initial admin setup.
- Create a repo and push.
- Clone to a second folder.
- Stop and start to prove data persists.
Recap
- Gitea is a lightweight self-hosted Git service.
- Named volumes keep data.
- HTTP push works for labs; SSH is next step.
- Always change the default ports if 3000 is taken.
- Backup the volume before upgrades.
Lab
- Finish all six projects on one practice machine.
- Keep a short branch-rule note next to your bare remote.
- Never store tokens in commit history.
- After each project, write one paragraph: what broke and how you fixed it.
- Re-clone once and confirm tags and hooks still make sense.
Practice task
Pick the conflict lab and the semver bump script. Prove both on a fresh clone of your bare remote.
Ravindra Bagale's Tip
Do the projects in order on one practice folder. Keep a notebook of the branch names that worked. Never commit secrets.
एक practice folder वर projects क्रमाने करा. काम केलेल्या branch names ची notebook ठेवा. Secrets कधीच commit करू नका.
एक practice folder पर projects क्रम से करें. काम आए branch names की notebook रखें. Secrets कभी commit न करें.
In short
- main stays shippable; work happens on branches.
- Hooks stop secrets before history.
- Conflict markers need a talk and a test.
- Semver tags beat final.zip names.
- Repo shape follows release cadence.
- Gitea with a named volume keeps private Git local.