--- title: "How to Install Gitea on Ubuntu 24.04 with Nginx and SSH" description: "Install Gitea on Ubuntu 24.04 with PostgreSQL, run it as a systemd service behind Nginx with Let's Encrypt, and push code over HTTPS and SSH to your Git server." category: "Web Applications Hosting" language: en source: https://cubepath.com/docs/web-applications-hosting/gitea-installation-lightweight-git-hosting updated: 2026-09-25 --- # How to Install Gitea on Ubuntu 24.04 with Nginx and SSH Gitea is a lightweight, self-hosted Git service written in Go. It offers repositories, pull requests, issues, a package registry and CI through Gitea Actions, while running comfortably on a small server. In this tutorial you will install the official Gitea binary on Ubuntu 24.04, store its data in PostgreSQL, publish the web interface over HTTPS with Nginx and Let's Encrypt, and clone repositories over SSH through the system OpenSSH server. ## Prerequisites To follow this guide you need: - A server running Ubuntu 24.04 LTS (x86_64), for example a CubePath VPS. 1 vCPU and 2 GB of RAM are enough for a small team. - A non-root user with `sudo` privileges. - A domain or subdomain, such as `git.your_domain`, with a DNS A record pointing to your server's public IP. This guide uses `your_domain` as a placeholder. - An SSH key pair on your local computer, to test Git over SSH at the end. ## Step 1 - Installing dependencies and opening the firewall Gitea needs Git on the server, and you will use `jq` to read the latest version number. Install them together with PostgreSQL and Nginx: ```bash sudo apt update sudo apt install -y git jq postgresql nginx ``` Allow SSH, HTTP and HTTPS through UFW. Gitea's own port (3000) stays closed; Nginx will proxy to it: ```bash sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable sudo ufw status ``` ```output Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere Nginx Full ALLOW Anywhere ``` ## Step 2 - Creating the PostgreSQL database Open a PostgreSQL shell as the `postgres` superuser: ```bash sudo -u postgres psql ``` Create a login role and a UTF-8 database owned by it. Replace `your_strong_password` with a long random password; you will enter it in the web installer later: ```sql CREATE ROLE gitea WITH LOGIN PASSWORD 'your_strong_password'; CREATE DATABASE gitea WITH OWNER gitea TEMPLATE template0 ENCODING 'UTF8'; \q ``` Test the connection over TCP, the same way Gitea will connect: ```bash psql -h 127.0.0.1 -U gitea -d gitea -c 'SELECT current_user;' ``` ```output current_user -------------- gitea (1 row) ``` ## Step 3 - Creating the git user and directories Gitea runs as a dedicated system user. Naming it `git` gives you clean SSH clone URLs such as `git@your_domain:user/repo.git`, because Gitea manages that user's `authorized_keys` file and the system OpenSSH server handles the connections: ```bash sudo adduser --system --shell /bin/bash --gecos 'Git Version Control' --group --disabled-password --home /home/git git ``` Create the working directory for repositories, attachments and logs, and the configuration directory. `/etc/gitea` is writable by the `git` group for now so the web installer can save `app.ini`: ```bash sudo mkdir -p /var/lib/gitea/{custom,data,log} sudo chown -R git:git /var/lib/gitea sudo chmod -R 750 /var/lib/gitea sudo mkdir /etc/gitea sudo chown root:git /etc/gitea sudo chmod 770 /etc/gitea ``` ## Step 4 - Downloading the Gitea binary Gitea publishes the current stable version in a small JSON file. Read it into a shell variable: ```bash GITEA_VERSION=$(curl -fsSL https://dl.gitea.com/gitea/version.json | jq -r '.latest.version') echo "$GITEA_VERSION" ``` The output is a version number such as `1.24.6`. If you prefer to pin a version, set `GITEA_VERSION` by hand from the releases listed at `https://dl.gitea.com/gitea/`. Download the binary and its SHA-256 checksum, then verify it: ```bash cd /tmp curl -fsSLO "https://dl.gitea.com/gitea/${GITEA_VERSION}/gitea-${GITEA_VERSION}-linux-amd64" curl -fsSLO "https://dl.gitea.com/gitea/${GITEA_VERSION}/gitea-${GITEA_VERSION}-linux-amd64.sha256" sha256sum -c "gitea-${GITEA_VERSION}-linux-amd64.sha256" ``` ```output gitea-1.24.6-linux-amd64: OK ``` Install it to `/usr/local/bin` and check that it runs: ```bash sudo install -m 755 "gitea-${GITEA_VERSION}-linux-amd64" /usr/local/bin/gitea gitea --version ``` ```output Gitea version 1.24.6 built with GNU Make ... ``` ## Step 5 - Running Gitea as a systemd service Create a unit file: ```bash sudo nano /etc/systemd/system/gitea.service ``` Paste the following. It starts Gitea after PostgreSQL, as the `git` user, with the paths created in Step 3: ```ini [Unit] Description=Gitea (Git with a cup of tea) After=network.target postgresql.service Wants=postgresql.service [Service] Type=simple User=git Group=git WorkingDirectory=/var/lib/gitea/ ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini Restart=always RestartSec=2s Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea [Install] WantedBy=multi-user.target ``` Load the unit, start Gitea and enable it at boot: ```bash sudo systemctl daemon-reload sudo systemctl enable --now gitea sudo systemctl status gitea ``` ```output ● gitea.service - Gitea (Git with a cup of tea) Loaded: loaded (/etc/systemd/system/gitea.service; enabled; preset: enabled) Active: active (running) since ... ``` Gitea is now listening on port 3000 in installation mode. Confirm it answers locally: ```bash curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3000 ``` ```output 200 ``` ## Step 6 - Configuring Nginx and HTTPS Create an Nginx site for Gitea: ```bash sudo nano /etc/nginx/sites-available/gitea ``` Add this server block, replacing `your_domain`. `client_max_body_size` controls the largest push or upload that Nginx accepts: ```nginx server { listen 80; listen [::]:80; server_name your_domain; client_max_body_size 512M; location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } ``` Enable it, remove the default site and reload Nginx: ```bash sudo ln -s /etc/nginx/sites-available/gitea /etc/nginx/sites-enabled/ sudo rm /etc/nginx/sites-enabled/default sudo nginx -t sudo systemctl reload nginx ``` Install Certbot and request a certificate. The Nginx plugin adds the TLS configuration and an HTTP to HTTPS redirect to this site: ```bash sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d your_domain ``` Check that renewal will work unattended: ```bash sudo certbot renew --dry-run ``` ```output Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/your_domain/fullchain.pem (success) ``` ## Step 7 - Completing the web installer Open `https://your_domain` in your browser. Gitea shows the **Initial Configuration** page. Fill in these fields and leave the rest at their defaults: | Field | Value | |---|---| | Database Type | PostgreSQL | | Host | `127.0.0.1:5432` | | Username / Database Name | `gitea` / `gitea` | | Password | the password from Step 2 | | Server Domain | `your_domain` | | SSH Server Port | `22` | | Gitea HTTP Listen Port | `3000` | | Gitea Base URL | `https://your_domain/` | Expand **Administrator Account Settings** and create your admin user. If you want only the admin to create accounts, open **Server and Third-Party Service Settings** and tick **Disable Self-Registration**. Click **Install Gitea**; after a few seconds you are logged in as the administrator. The installer has written `/etc/gitea/app.ini`. Remove write access to the configuration now that it exists: ```bash sudo chmod 750 /etc/gitea sudo chmod 640 /etc/gitea/app.ini ``` By default Gitea listens on all interfaces. Bind it to localhost so it is only reachable through Nginx, even if the firewall changes later. Open the file: ```bash sudo nano /etc/gitea/app.ini ``` In the `[server]` section add, or edit, this line: ```ini HTTP_ADDR = 127.0.0.1 ``` Restart Gitea: ```bash sudo systemctl restart gitea ``` ## Step 8 - Pushing code over HTTPS and SSH In the web interface, click your avatar, open **Settings > SSH / GPG Keys**, click **Add Key** and paste the contents of your local public key (for example `~/.ssh/id_ed25519.pub`). Then click **+ > New Repository**, name it `hello`, and create it. From your local computer, test SSH authentication: ```bash ssh -T git@your_domain ``` ```output Hi there, your_user! You've successfully authenticated with the key named laptop, but Gitea does not provide shell access. ``` Create a local repository and push it: ```bash mkdir hello && cd hello git init -b main echo "# hello" > README.md git add README.md git commit -m "Initial commit" git remote add origin git@your_domain:your_user/hello.git git push -u origin main ``` Reload the repository page in your browser and the README appears. HTTPS cloning works too with `git clone https://your_domain/your_user/hello.git`. ## Step 9 - Backing up and upgrading Gitea `gitea dump` packs the repositories, the database and the configuration into a single zip file. Create a directory owned by the `git` user and take a first dump: ```bash sudo mkdir -p /var/backups/gitea sudo chown git:git /var/backups/gitea sudo chmod 700 /var/backups/gitea cd /var/backups/gitea sudo -u git gitea dump -c /etc/gitea/app.ini -w /var/lib/gitea -f "gitea-dump-$(date +%F).zip" ls -lh /var/backups/gitea ``` To run it nightly and keep two weeks of dumps, create a cron file: ```bash sudo nano /etc/cron.d/gitea-dump ``` ```text 30 2 * * * git cd /var/backups/gitea && /usr/local/bin/gitea dump -c /etc/gitea/app.ini -w /var/lib/gitea -f "gitea-dump-$(date +\%F).zip" >/dev/null 2>&1 45 3 * * * git find /var/backups/gitea -name 'gitea-dump-*.zip' -mtime +14 -delete ``` Copy the dumps to another location regularly, such as object storage. To upgrade, take a dump first, since Gitea migrates the database automatically on start. Then run the `GITEA_VERSION`, download and `sha256sum` commands from Step 4 (not the `install` command) to fetch the new release, and replace the binary while the service is stopped: ```bash sudo systemctl stop gitea sudo install -m 755 "/tmp/gitea-${GITEA_VERSION}-linux-amd64" /usr/local/bin/gitea sudo systemctl start gitea ``` ## Troubleshooting - **502 Bad Gateway**: Gitea is not running. Check `sudo journalctl -u gitea -n 50`. A common cause is `app.ini` not being readable by the `git` group after changing permissions. - **`ssh -T` asks for a password**: the key was not added in Gitea, or the `git` user's shell is not `/bin/bash`. Check `/home/git/.ssh/authorized_keys` contains a line with `gitea serv`. - **Push fails with "413 Request Entity Too Large"**: increase `client_max_body_size` in the Nginx site and reload Nginx. - **Links and clone URLs show `http://` or port 3000**: fix `ROOT_URL` in the `[server]` section of `app.ini` to `https://your_domain/` and restart Gitea. ## Conclusion You now run Gitea on Ubuntu 24.04 with PostgreSQL, HTTPS through Nginx, SSH clones through the system OpenSSH server and nightly dumps. Next, you can enable Gitea Actions and register a runner to build your code, configure a mail server in the `[mailer]` section of `app.ini` for notifications, or mirror existing GitHub repositories with **+ > New Migration**.