This article is about my deployment experience of Gitea, a self-hosted GitHub like code hosting service which also support CI/CD pipeline workflows like GitHub Action.
Why I Need It
Recently, I was searching for a way to host Obsidian vault on the Internet. There is an official way to achieve this using Obsidian Publish, however, it’s not free.
So, I keep searching the Internet and finally saw this Reddit post discussing using SSG (static site generator) to host Obsidian vault, that’s exactly what I want. I randomly pick one in the comments section called Quartz, tried it out on my local computer, and seems that it works well with Obsidian.
Now the only thing I need to do is to have a self-host deployment tool like GitHub actions to automatically deploy my code every time I update my content. And my basic thought is shown by the image below:

Installation
Write at the beginning, it’s highly recommended to follow the official installation guide to finish your docker installation.
Gitea Installation
Installation Gitea using docker is quite straightforward, just follow its official guide. I choose to use docker compose method to create the container.
- Create a new unprivileged user
gitea(or any other name you want) - Create a new folder, here I use the
giteauser folder - Write your
docker-compose.ymlinside the folder - Run
docker compose up -d
In linux, we could create user using following command:
sudo adduser gitea
After create the user, we could go to its home directory /home/gitea and create a new docker-compose.yml file. Below is my own one for Gitea docker container. For more info, please read Official Gitea Docker Installation Guide on their website.
version: "3"
networks:
gitea:
external: false
services:
server:
image: docker.gitea.com/gitea:latest
container_name: gitea
environment:
- USER=gitea
- USER_UID=1005
- USER_GID=1005
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=db:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=gitea
restart: always
networks:
- gitea
volumes:
- /sto/gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "30015:3000"
- "30016:22"
depends_on:
- db
db:
image: docker.io/library/postgres:14
restart: always
environment:
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=gitea
- POSTGRES_DB=gitea
networks:
- gitea
volumes:
- ./postgres:/var/lib/postgresql/data
Action Runner Installation
Finishing Gitea installation isn’t an end: The action feature doesn’t come with Gitea docker image installation. For the feature I depicted above to work, we also need to configure a runner to execute actions defined in our repository.
Still, Gitea have a well-documented official Gitea Act Runner guide for this procedure, please check it out. One thing to notice in this guide is that if we want to use actions/cache in our workflow, then the procedures in “Advanced Configurations” is needed in order to make cache action work.
For demostration, I will put on my own docker-compose.yml and config.yml for Act Runner:
services:
runner:
image: docker.io/gitea/act_runner:latest
ports:
- "30017:30017"
environment:
CONFIG_FILE: /config.yaml
GITEA_INSTANCE_URL: "https://my-host.example.com"
GITEA_RUNNER_REGISTRATION_TOKEN: "<my_reg_token>"
GITEA_RUNNER_NAME: "example-runner"
GITEA_RUNNER_LABELS: ""
volumes:
- ./config.yaml:/config.yaml
- /var/run/docker.sock:/var/run/docker.sock
- ./runner_output:/runner_output
restart: always
cache:
enabled: true
dir: ""
host: "<my_host_public_ipv4_addr>"
port: 30017
Note that mapped port is the same, just like above we have mapping "30017:30017".
Enjoy Self-Hosted Actions
After all the steps above, the Gitea should now run on the host. And it should be able to execute actions defined in the .github/workflows directory of your project:

As shown above, I used this self-host Gitea to auto-build my Leetcode note website codenotes.nfblogs.com, every time I push to this remote repo, Gitea will automatically run npx quartz build to generate public folder and then use FTP to deploy to target directory on my web server.

Things That Do Not Resolved
IPv6 Support
One of the things that confuse me is that IPv6 doesn’t work inside action.
I try using IPv6 for FTP connection in my actions, but it failed, while IPv4 version works smoothly, being able to connect to the server and copy artifacts to the target directory.
- Since the runner itself runs inside Docker, maybe we should check out if Docker network support IPv6 in the first place
- Is the IPv6 on my host has been configured correctly?
- Does the FTP server really support working on IPv6?
Better Deployment Method
For me, since these are for personal use only, the Act Runner, Web Server and Gitea Server are actually running at the same remote server. So, using FTP to connect from Act Runner Docker to my host seems a little bit wired and unnecessary. Maybe I should find a better way to deploy the artifact public folder after action successfully generate it using npm quartz build.
This part serves as a reminder to myself, maybe I will go back to check and fix these things in the future.