← Back to Blog

Forgejo on Debian 13 with Rootless Podman

Full guide for installing Forgejo as a rootless Podman container with Caddy as reverse proxy, systemd integration via Quadlets, and SSH access.

Tested environment:

  • Debian 13 (Trixie)
  • Podman 5.4.2
  • VPS: 4 CPU cores, 4 GB RAM (Hetzner)
  • Caddy as reverse proxy

1. Install prerequisites

sudo apt install podman uidmap passt slirp4netns
  • uidmap — required for rootless Podman (user namespace mapping)
  • passt — network backend for rootless containers
  • slirp4netns — alternative network backend

2. Add hostname to /etc/hosts

To avoid sudo warnings (unable to resolve host):

sudo nano /etc/hosts

Add the line:

127.0.0.1 <hostname>

3. Create a dedicated user

Forgejo runs rootless under its own git user:

sudo useradd -m -s /bin/bash git
sudo loginctl enable-linger git

enable-linger ensures the systemd user service keeps running without an active login session.


4. Switch to the git user

sudo su - git

5. Create directories

mkdir -p ~/forgejo-data
mkdir -p ~/.config/containers/systemd

6. Pull the image

podman pull codeberg.org/forgejo/forgejo:10-rootless

The image comes directly from Codeberg (not Docker Hub).


7. Set volume permissions

The rootless image runs internally as a different UID. Permissions need to be set with podman unshare:

podman unshare chown -R 1000:1000 ~/forgejo-data

8. Create the Quadlet file

Quadlets are systemd unit files for Podman containers.

cat > ~/.config/containers/systemd/forgejo.container << 'EOF'
[Unit]
Description=Forgejo
After=network-online.target

[Container]
ContainerName=forgejo
Image=codeberg.org/forgejo/forgejo:10-rootless
Network=host

Volume=%h/forgejo-data:/var/lib/gitea
Volume=/etc/timezone:/etc/timezone:ro
Volume=/etc/localtime:/etc/localtime:ro

Label=io.containers.autoupdate=registry

[Service]
Restart=always

[Install]
WantedBy=default.target
EOF

Important: Network=host is required for SSH to work from outside. With pasta (the default rootless network backend), external SSH connections are not forwarded correctly.


9. Start the container

systemctl --user daemon-reload
systemctl --user start forgejo.service
systemctl --user status forgejo.service

10. Initial setup in the browser

Open http://VPS-IP:3000 — the setup wizard appears.

Recommended settings:

  • Database type: SQLite (sufficient for personal use)
  • Domain: git.your-domain.com
  • Root URL: https://git.your-domain.com/
  • Create an admin account

11. Configure app.ini

After initial setup, adjust the configuration:

podman unshare nano ~/forgejo-data/custom/conf/app.ini

Add to the [server] block:

[server]
SSH_DOMAIN = git.your-domain.com
SSH_PORT = 2222
SSH_LISTEN_PORT = 2222
START_SSH_SERVER = true
BUILTIN_SSH_SERVER_USER = git

In the [service] block:

[service]
DISABLE_REGISTRATION = true

Restart Forgejo:

systemctl --user restart forgejo.service

12. Set up Caddy as reverse proxy

As a regular user, create a new file:

sudo nano /etc/caddy/sites/forgejo.caddy

Contents:

git.your-domain.com {
    reverse_proxy localhost:3000
}

Reload Caddy:

sudo systemctl reload caddy

Caddy automatically obtains a TLS certificate from Let’s Encrypt.


13. Configure DNS

In Cloudflare (or another DNS provider):

TypeNameContentProxy
A*VPS IP addressDNS only
A@VPS IP addressDNS only

Important: Cloudflare proxy (orange cloud) must be off so Caddy can obtain the TLS certificate itself.


14. Firewall (Hetzner)

In the Hetzner Cloud Console → Firewall → Inbound Rules:

ProtocolPortSource
TCP800.0.0.0/0, ::/0
TCP4430.0.0.0/0, ::/0
TCP22220.0.0.0/0, ::/0
TCP220.0.0.0/0, ::/0

15. Set up SSH key

Display the local SSH key:

cat ~/.ssh/id_ed25519.pub

Add it in Forgejo: Profile → Settings → SSH / GPG Keys → Add Key

Test SSH:

ssh -p 2222 -T git@git.your-domain.com
# Hi there, username! You've successfully authenticated...

Local ~/.ssh/config:

Host git.your-domain.com
    Port 2222
    User git
    IdentityFile ~/.ssh/id_ed25519

16. Push the repository

cd /path/to/project
git remote set-url origin ssh://git@git.your-domain.com:2222/username/repo.git
git push -u origin main

Known issues and solutions

newuidmap: executable file not found

sudo apt install uidmap

pasta: executable file not found

sudo apt install passt

Permission denied on volume

podman unshare chown -R 1000:1000 ~/forgejo-data

SSH not working from outside

Rootless Podman with pasta does not forward external SSH connections. Fix: use Network=host in the Quadlet file.

fail2ban blocks SSH connections

After too many failed attempts the IP gets banned:

sudo fail2ban-client status sshd
sudo fail2ban-client set sshd unbanip YOUR_IP

To exclude port 2222 from fail2ban, create /etc/fail2ban/jail.local:

[sshd]
port = ssh
sudo systemctl restart fail2ban

Two containers running simultaneously

When renaming, old containers can keep running and occupy the port:

sudo podman ps  # show all running containers
sudo podman rm -f <old-container-id>
sudo podman rmi localhost/old-image-name:latest

Podman build uses old cache

The build service needs to use --no-cache. In /etc/systemd/system/podman-compose@.service:

ExecStart=/usr/bin/podman-compose up -d --build --no-cache

Useful commands

# Check status (as git user)
systemctl --user status forgejo.service

# View logs
journalctl --user -u forgejo.service -f

# Restart container
systemctl --user restart forgejo.service

# Edit configuration
podman unshare nano ~/forgejo-data/custom/conf/app.ini

# Automatic updates
podman auto-update

# Show running containers
podman ps

Resource usage

Forgejo is very lightweight — roughly 130–145 MB RAM in operation with 4 GB available.

← Back to Blog