Skip to content
+1 (813) 212-3723 support@layeronecloud.com
Self-hosting VPS guides

How to Host Forgejo on a VPS with Docker and HTTPS

Deploy Forgejo 16 on an Ubuntu VPS with Docker, SQLite, Caddy HTTPS, Git-over-SSH, explicit firewall rules, upgrades, and restorable backups.

7 min read

Forgejo is a self-hosted Git forge for repositories, issues, pull requests, packages, and Actions. This guide builds a small, production-minded Forgejo VPS: the web listener stays on loopback, Caddy terminates HTTPS, Git-over-SSH uses a separate public port, and all persistent state lives under /opt/forgejo.

The commands target Ubuntu 24.04 LTS on x86-64, Docker Engine with the Compose plugin, and Forgejo's current 16 release channel. Forgejo documents that a major-version change requires a deliberate upgrade and verification, so the Compose file follows major 16 instead of an unbounded latest tag.

Forgejo VPS requirements and LayerOne cost

Item Practical starting point
Minimum RAM 1 GB for a low-activity personal instance using SQLite; do not run Actions jobs on the same server
Recommended RAM 2 GB for several users, routine clones, package metadata, and update headroom
Expected CPU usage Commonly about 1–5% of one vCPU while idle; clones, archives, indexing, LFS, and upgrades can use the full vCPU temporarily
Storage requirement Allow at least 10 GB free for the OS, images, logs, and temporary archives; repository, LFS, package, attachment, and Actions data then grows with actual use
Exact LayerOne SKU gc.micro (layerone-starter): 1 vCPU, 2 GB RAM, 40 GB disk
Expected monthly cost $0.0068/hour, about $5.00 for 730 hours at the catalog's monthly equivalent

Those CPU figures are planning estimates, not guarantees. A public forge with large repositories, CI jobs, many concurrent users, or container/package registries needs workload measurements and usually a larger plan. Keep at least 20% of the disk free and alert on repository and Docker growth.

LayerOne meters the VPS hourly from prepaid credit. The monthly figure assumes roughly 730 billable hours and excludes taxes, optional services, third-party software, and transfer beyond the account allowance. Review live values on Pricing before deploying.

1. Prepare the VPS, DNS, and Docker

Deploy Ubuntu 24.04, complete Your first hour on a new server, and create an A record such as git.example.com pointing to the VPS public IPv4 address. Install Docker Engine from Docker's official repository by following Docker on a VPS.

Confirm the prerequisites:

docker version
docker compose version
getent ahostsv4 git.example.com

The DNS result must contain this VPS address before Caddy can issue a public certificate.

2. Create Forgejo's persistent directory

The official Forgejo image runs the application with UID and GID 1000 in this configuration. Give that identity ownership of the bind mount:

sudo install -d -m 0750 -o 1000 -g 1000 /opt/forgejo/data
cd /opt/forgejo

Create /opt/forgejo/compose.yaml:

sudo tee /opt/forgejo/compose.yaml >/dev/null <<'YAML'
services:
  forgejo:
    image: codeberg.org/forgejo/forgejo:16
    container_name: forgejo
    restart: unless-stopped
    environment:
      USER_UID: "1000"
      USER_GID: "1000"
      FORGEJO__database__DB_TYPE: sqlite3
      FORGEJO__server__DOMAIN: git.example.com
      FORGEJO__server__ROOT_URL: https://git.example.com/
      FORGEJO__server__SSH_DOMAIN: git.example.com
      FORGEJO__server__SSH_PORT: "2222"
      FORGEJO__service__DISABLE_REGISTRATION: "true"
    ports:
      - "127.0.0.1:3000:3000"
      - "2222:22"
    volumes:
      - ./data:/data
      - /etc/localtime:/etc/localtime:ro
YAML

sudo docker compose -f /opt/forgejo/compose.yaml config
sudo docker compose -f /opt/forgejo/compose.yaml up -d
sudo docker compose -f /opt/forgejo/compose.yaml logs --tail=100 forgejo

Replace git.example.com everywhere before starting. The loopback binding on port 3000 prevents clients from bypassing the HTTPS proxy. Port 2222 is the public Git SSH endpoint because the host already uses port 22 for administration. If every user will clone over HTTPS, remove the 2222:22 mapping and its firewall rule.

SQLite is supported by Forgejo and is a sensible low-to-moderate activity starting point. Choose PostgreSQL before onboarding if you already expect high concurrency; changing an established instance's database later is a migration, not a Compose toggle.

3. Set both firewalls

Replace the documentation address below with the single public CIDR from which you administer the server:

ADMIN_CIDR="198.51.100.24/32"
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from "$ADMIN_CIDR" to any port 22 proto tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 2222/tcp
sudo ufw enable
sudo ufw status numbered

Apply the same inbound policy in the LayerOne cloud firewall: allow TCP 22 only from the administrator CIDR, allow TCP 80 and 443 from 0.0.0.0/0, and allow TCP 2222 from the networks that need Git SSH. There should be no public rule for TCP 3000. The cloud firewall filters before Docker's host rules; see The cloud firewall.

Port Exposure Purpose
TCP 22 Administrator CIDR only Host SSH
TCP 80 Public ACME validation and redirect to HTTPS
TCP 443 Public Forgejo web UI, API, and Git over HTTPS
TCP 2222 Public or trusted source ranges Git over SSH to the Forgejo container
TCP 3000 Loopback only Caddy-to-Forgejo HTTP backend

4. Put Forgejo behind Caddy HTTPS

Install Caddy using Reverse proxy on a VPS. On a new Caddy host, use this /etc/caddy/Caddyfile; merge the site block into the existing file if Caddy already serves another application:

git.example.com {
    encode zstd gzip
    reverse_proxy 127.0.0.1:3000
}

Validate and reload without dropping the working process:

sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
sudo journalctl -u caddy -n 50 --no-pager

Caddy obtains and renews the certificate automatically when DNS is correct and ports 80 and 443 reach this host.

5. Complete onboarding and verify both protocols

Open https://git.example.com/, keep SQLite selected, and create the first administrator. Public registration is disabled by the environment setting; create additional users as an administrator or deliberately change the policy.

Create a small private repository, then verify HTTPS and SSH from another machine:

curl -I https://git.example.com/
ssh -T -p 2222 git@git.example.com
git clone https://git.example.com/OWNER/REPOSITORY.git

The SSH command may exit without opening a shell; it should identify the Forgejo account associated with the uploaded public key. A remote connection to SERVER_IP:3000 must fail.

6. Back up and restore the whole instance

Forgejo data includes repositories, the SQLite database, configuration, attachments, LFS objects, packages, keys, and secrets. For a small SQLite instance, the least ambiguous backup is a brief cold archive of the entire persistent directory:

cd /opt/forgejo
sudo docker compose stop forgejo
sudo tar --acls --xattrs -C /opt -czf "/root/forgejo-$(date +%F).tar.gz" forgejo
sudo docker compose start forgejo
sudo docker compose ps

Copy the archive off the VPS, encrypt it at rest, and set a retention policy. Test a restore on an isolated server: stop Forgejo, restore the forgejo directory under /opt, restore ownership, start the same major image, and clone a repository. Forgejo also provides the official forgejo dump command for application-aware exports. A backup left only on this VPS does not survive loss of the VPS; LayerOne does not take server backups. See Back up your own data.

7. Upgrade without crossing a major accidentally

Read the Forgejo release notes, take a fresh backup, and update within major 16:

cd /opt/forgejo
sudo docker compose pull
sudo docker compose up -d
sudo docker compose ps
sudo docker compose logs --tail=100 forgejo
curl -I https://git.example.com/

For a future 16 to 17 change, read the upstream upgrade guide first, change the image tag deliberately, and retain a restorable pre-upgrade archive. Never run untrusted Actions jobs on this application host; give automation a separate runner VPS with only the network access and repository scope it needs.

Security checklist

  • Keep public registration disabled unless you are prepared to moderate abuse.
  • Require strong unique passwords and two-factor authentication for administrators.
  • Keep port 3000 private and never publish the SQLite file or /data through a web server.
  • Restrict Git SSH by source network when practical; remove it entirely if unused.
  • Treat repository mirrors and webhooks as outbound network access from the server.
  • Review disk use with df -h /opt/forgejo and docker system df.
  • Back up before every major upgrade and prove that a repository can be restored.

Official references

Still stuck

Chat with us from the portal.

Ask the assistant from the Chat bar. During business hours you can ask for a person and a human joins live.