Gitea 28.2.0
Overview
Gitea is a lightweight, self-hosted Git service written in Go. It gives a team Git hosting with a familiar web interface: repositories with branch protection and code review through pull requests, issues and milestones, a wiki per repository, releases, a package registry, organisations and teams, and Gitea Actions for CI.
This guide covers launching and using the Gitea 28.2.0 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Apache Subversion (SVN) and CI/CD Tools listing on AWS Marketplace, and you pick it as the software version when you subscribe and launch. The image runs the official Gitea 28.2.0 release with a SQLite database on the instance, so there is no separate database server to run.
What you get:
- HTTPS from the first boot. nginx serves Gitea on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
- An administrator and secrets unique to your instance. First boot creates the database and the administrator
gitea_adminwith a password generated on your instance. It also generates Gitea's secret key, internal token and token signing secrets. Nothing secret is baked into the image, and the settings file holds no secret at all. - Private by default. Sign-in is required to see anything, visitors cannot register their own accounts (the administrator creates them), and Gitea's web installer is locked.
- Fail closed. Gitea refuses to start until first boot has finished, and so does nginx.
- Gitea on loopback only. Gitea's web server listens on
127.0.0.1:3000behind nginx. Git over SSH uses Gitea's built-in SSH server on port 2222 (key authentication only, no shell), so port 22 stays your administrator SSH. The open ports are 22, 80, 443 and 2222. - Protection against server-side request forgery. Webhooks, repository migrations and mirrors cannot reach private, loopback or link-local addresses such as the instance metadata service. Gitea and nginx are also blocked from the metadata service at the kernel level. The AMI requires IMDSv2.
- Hardened defaults. Gitea runs as its own unprivileged
gituser in a systemd sandbox that can write only/var/lib/gitea. Its public address comes from your configuration, never from a request, and requests for a host name the instance does not serve are dropped. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: Gitea 28.2.0 (the official gitea-28.2.0-linux-amd64 release binary, checksum verified), git, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu. Architecture: x86_64. Recommended instance type: m5.large.
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair, and a VPC subnet with a route to the internet (or a private route you use for management).
- A security group that allows TCP 22 (administrator SSH), TCP 443 (Gitea), TCP 80 (redirects to HTTPS) and TCP 2222 (Git over SSH) from your own networks only.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Gitea 28.2.0 on Ubuntu 24.04 as the software version and select your Region. Then choose Launch through EC2.
Choose m5.large, your key pair and a security group as described above. Repositories and the database live under /var/lib/gitea on the root volume, so size the root volume for the repositories you expect to host.
To launch with the AWS CLI instead, replace the values in angle brackets:
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=50,VolumeType=gp3}' \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=gitea}]'Connect to your instance
Connect over SSH as the ubuntu user, which is the SSH user for Gitea 28.2.0 on Ubuntu 24.04.
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot takes a few seconds. When it finishes, the three units are active:
systemctl is-active gitea-firstboot gitea nginxactive active activeCheck the listeners. Gitea's web server listens on loopback only, its SSH server on 2222, and nginx serves ports 80 and 443. The second loopback port is Gitea's internal proxy for outbound Git operations.
sudo ss -tlnp | awk 'NR==1 || /gitea|nginx|sshd/' | awk '{print $1, $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g'State Local Peer LISTEN 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx")) LISTEN 127.0.0.1:44181 users:(("gitea")) LISTEN 0.0.0.0:22 users:(("sshd"),("systemd")) LISTEN 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx")) LISTEN 127.0.0.1:3000 users:(("gitea")) LISTEN [::]:443 users:(("nginx"),("nginx"),("nginx")) LISTEN *:2222 users:(("gitea")) LISTEN [::]:22 users:(("sshd"),("systemd")) LISTEN [::]:80 users:(("nginx"),("nginx"),("nginx"))Confirm the version on the image:
gitea --versiongitea version 28.2.0 built with go1.27.2Get your URL and password
Read the credentials file as root:
sudo cat /root/gitea-credentials.txtThe file holds the address Gitea is served as (your instance's public DNS name by default), the administrator user, the password generated on this instance, and the form of an SSH clone URL:
GITEA_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ GITEA_ADMIN_USER=gitea_admin GITEA_ADMIN_PASSWORD=<24 letters and digits> GITEA_SSH_PORT=2222 GITEA_SSH_CLONE=ssh://git@ec2-<public-ip-dashed>.<region>.compute.amazonaws.com:2222/<owner>/<repo>.gitSign in from your browser
Open GITEA_URL in your browser. The certificate is self-signed and generated on your instance, so accept the browser warning once, or install your own certificate (Step 9). Sign in with gitea_admin and the password from Step 4.
The sign-in page has no Register link, because on this image only the administrator can create accounts. After you sign in, your dashboard shows your activity and your repositories.
To change the administrator password, open the user menu at the top right, then choose Settings, Account.
Create a repository and push to it
In the browser, choose + at the top right, then New Repository. Give the repository a name and choose Create Repository. The repository page shows the clone URLs under Code, for HTTPS and for SSH (port 2222).
From your workstation, push an existing project over HTTPS. Git asks for your Gitea user name and password. While the certificate is self-signed, tell Git to accept it for this host only:
git -c http.sslVerify=false push https://<gitea-host>/<owner>/<repo>.git mainAlternatively, add your SSH public key in the browser (user menu, Settings, SSH / GPG Keys, Add Key) and use the SSH URL on port 2222:
git clone ssh://git@<gitea-host>:2222/<owner>/<repo>.gitYou can try both methods from the instance itself. This command creates a private repository called demo through Gitea's API. The password is handed to curl in a file, so it never appears on a command line:
H=$(sudo sed -n 's#^GITEA_URL=https://\([^/]*\)/.*#\1#p' /root/gitea-credentials.txt)
CFG=$(mktemp)
sudo sed -n 's/^GITEA_ADMIN_PASSWORD=\(.*\)/user = "gitea_admin:\1"/p' /root/gitea-credentials.txt > "$CFG" curl -sk --resolve "$H:443:127.0.0.1" -K "$CFG" -H 'Content-Type: application/json' -o /dev/null -w 'create repository: %{http_code}\n' \
-X POST "https://$H/api/v1/user/repos" -d '{"name":"demo","private":true}' rm -f "$CFG"create repository: 201Push a first commit to the repository over HTTPS. Git reads the password through a small helper script, again never from a command line:
H=$(sudo sed -n 's#^GITEA_URL=https://\([^/]*\)/.*#\1#p' /root/gitea-credentials.txt)
W=$(mktemp -d); sudo sed -n 's/^GITEA_ADMIN_PASSWORD=//p' /root/gitea-credentials.txt > "$W/pw" printf '#!/bin/sh\ncase "$1" in Username*) echo gitea_admin ;; *) cat "%s/pw" ;; esac\n' "$W" > "$W/askpass"; chmod 700 "$W/askpass" git -C "$W" init -q -b main demo && echo "# demo" > "$W/demo/README.md" git -C "$W/demo" add README.md && git -C "$W/demo" -c user.name=Admin -c user.email=admin@example.com commit -qm "first commit" GIT_ASKPASS="$W/askpass" git -C "$W/demo" -c http.sslVerify=false -c "http.curloptResolve=$H:443:127.0.0.1" push -q "https://$H/gitea_admin/demo.git" main && echo "pushed over HTTPS" rm -rf "$W"pushed over HTTPSNext, add an SSH key for gitea_admin and clone the repository back over SSH on port 2222:
H=$(sudo sed -n 's#^GITEA_URL=https://\([^/]*\)/.*#\1#p' /root/gitea-credentials.txt)
W=$(mktemp -d); ssh-keygen -q -t ed25519 -N '' -C demo -f "$W/id"
sudo sed -n 's/^GITEA_ADMIN_PASSWORD=\(.*\)/user = "gitea_admin:\1"/p' /root/gitea-credentials.txt > "$W/cfg" curl -sk --resolve "$H:443:127.0.0.1" -K "$W/cfg" -H 'Content-Type: application/json' -o /dev/null -w 'add SSH key: %{http_code}\n' \
-X POST "https://$H/api/v1/user/keys" -d "{\"title\":\"demo\",\"key\":\"$(cat "$W/id.pub")\"}" GIT_SSH_COMMAND="ssh -i $W/id -o HostName=127.0.0.1 -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=$W/known_hosts" \
git clone -q "ssh://git@$H:2222/gitea_admin/demo.git" "$W/demo" && echo "cloned over SSH: $(cat "$W/demo/README.md")" rm -rf "$W"add SSH key: 201 cloned over SSH:
# demoThe first time you connect over SSH from your workstation, compare the host key fingerprint with the one your instance generated. Display it on the instance:
sudo ssh-keygen -lf /var/lib/gitea/data/ssh/gitea.ed25519.pub256 SHA256:<fingerprint of your instance> no comment (ED25519)Users, organisations and administration
Sign in as gitea_admin, open the user menu and choose Site Administration. The dashboard shows maintenance operations and system status.
To add a user, choose Identity & Access, User Accounts, Create User Account. The user can change the password you give them at first sign-in. To create organisations and teams, choose +, New Organization.
To let people register their own accounts instead, set DISABLE_REGISTRATION = false (and SHOW_REGISTRATION_BUTTON = true) in the [service] section of /etc/gitea/app.ini. To let visitors browse public repositories without signing in, set REQUIRE_SIGNIN_VIEW = false. Edit the file with sudo nano /etc/gitea/app.ini, then restart Gitea:
sudo systemctl restart giteaSecurity checks
Run these checks on the instance. Port 80 sends browsers to HTTPS, nothing is visible without signing in, and a visitor cannot register. For a host name the instance does not serve, nginx closes the connection (000 means no answer):
H=$(sudo sed -n 's#^GITEA_URL=https://\([^/]*\)/.*#\1#p' /root/gitea-credentials.txt)
curl -s -o /dev/null -w 'port 80: %{http_code} -> %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -sk -o /dev/null -w 'sign in page: %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/user/login"
curl -sk -o /dev/null -w 'explore without signing in: %{http_code} -> %{redirect_url}\n' --resolve "$H:443:127.0.0.1" "https://$H/explore/repos"
curl -sk -o /dev/null -w 'self registration: %{http_code}\n' --resolve "$H:443:127.0.0.1" -X POST "https://$H/user/sign_up" \
-d 'user_name=visitor&email=visitor%40example.com&password=Visitor12345x&retype=Visitor12345x'
curl -s -o /dev/null -w 'unknown host name: %{http_code}\n' -H 'Host: example.com' http://127.0.0.1/ || trueport 80: 301 -> https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ sign in page: 200 explore without signing in: 303 -> https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/user/login?redirect_to=%2Fexplore%2Frepos self registration: 403 unknown host name: 000Gitea refuses to fetch from private and metadata addresses. A migration that asks it to import from the instance metadata service is refused:
H=$(sudo sed -n 's#^GITEA_URL=https://\([^/]*\)/.*#\1#p' /root/gitea-credentials.txt)
CFG=$(mktemp)
sudo sed -n 's/^GITEA_ADMIN_PASSWORD=\(.*\)/user = "gitea_admin:\1"/p' /root/gitea-credentials.txt > "$CFG" curl -sk --resolve "$H:443:127.0.0.1" -K "$CFG" -H 'Content-Type: application/json' -w ' (HTTP %{http_code})\n' \
-X POST "https://$H/api/v1/repos/migrate" -d '{"clone_addr":"http://169.254.169.254/latest/meta-data/","repo_name":"metadata-test","service":"git"}' rm -f "$CFG"{"message":"You can not import from disallowed hosts.","url":"https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/api/swagger"} (HTTP 422)These settings produce that behavior. Gitea listens on loopback and builds its links from its configured address only. It trusts forwarded client addresses from nginx on 127.0.0.1 only and keeps the installer locked. It blocks outbound requests to non-public addresses (EGRESS_MODE = lax with no allow list) and keeps registration off with sign-in required:
sudo grep -E '^(HTTP_ADDR|PUBLIC_URL_DETECTION|SSH_PORT|INSTALL_LOCK|REVERSE_PROXY_TRUSTED_PROXIES|EGRESS_MODE|BLOCKED_HOST_LIST|DISABLE_REGISTRATION|REQUIRE_SIGNIN_VIEW) ' /etc/gitea/app.iniHTTP_ADDR = 127.0.0.1 PUBLIC_URL_DETECTION = never SSH_PORT = 2222 INSTALL_LOCK = true REVERSE_PROXY_TRUSTED_PROXIES = 127.0.0.1/32 EGRESS_MODE = lax EGRESS_MODE = lax BLOCKED_HOST_LIST = 169.254.0.0/16 DISABLE_REGISTRATION = true REQUIRE_SIGNIN_VIEW = trueThe secrets are generated on this instance and live in their own files, readable by root and the git user only:
sudo ls -l /etc/gitea/secrets | awk 'NR>1 {print $1, $3, $4, $NF}'-rw-r----- root git internal_token -rw-r----- root git jwt_secret -rw-r----- root git lfs_jwt_secret -rw-r----- root git secret_keyGitea runs unprivileged in a sandbox, and neither Gitea nor nginx can reach the instance metadata service:
systemctl show gitea -p User -p ProtectSystem -p NoNewPrivileges -p IPAddressDeny
systemctl show nginx -p IPAddressDenyIPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32 User=git ProtectSystem=strict NoNewPrivileges=yes IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32The certificate is generated on this instance and names its own addresses:
H=$(sudo sed -n 's#^GITEA_URL=https://\([^/]*\)/.*#\1#p' /root/gitea-credentials.txt)
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltNamesubject=CN = ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhostYour own DNS name and certificate
Point a DNS record at the instance, then set the name Gitea is served as. The command below mints a new self-signed certificate for the name and updates nginx. It also updates Gitea's DOMAIN, ROOT_URL and SSH_DOMAIN (so clone URLs use the name) and the URL in /root/gitea-credentials.txt. It restarts Gitea and keeps the name across reboots:
sudo gitea-set-hostname <your-domain>To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx. Running gitea-set-hostname again replaces it with a self-signed certificate:
sudo install -m 0644 <your-domain>.crt /etc/nginx/ssl/gitea.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/ssl/gitea.key
sudo nginx -t && sudo systemctl reload nginxYour data, backups and upgrades
Repositories, LFS objects, attachments, packages and the SQLite database live under /var/lib/gitea/data:
sudo ls /var/lib/gitea/dataactions_artifacts actions_log attachments avatars gitea-repositories gitea.db home indexers jwt lfs packages queues repo-archive repo-avatars sessions ssh tmpBack up /var/lib/gitea and /etc/gitea (the settings and the secrets files) together, or snapshot the root volume. Gitea's own gitea dump command writes a single archive of the database, repositories and settings. Run it as the git user and keep a copy of /etc/gitea/secrets with it:
sudo -u git /usr/local/bin/gitea dump --config /etc/gitea/app.ini --file /var/lib/gitea/gitea-dump.zipUbuntu's unattended upgrades keep the operating system, git and nginx patched.
To move to a newer Gitea release later, first read the release notes for breaking changes. Then snapshot the instance and download the new gitea-<version>-linux-amd64 binary from the Gitea releases page. Check it against the published .sha256, replace /usr/local/bin/gitea and run sudo systemctl restart gitea. Gitea migrates the database on start.
Server components
These are the locations of the main components on the instance.
- Gitea binary: /usr/local/bin/gitea
- Settings: /etc/gitea/app.ini
- Secrets (generated on first boot): /etc/gitea/secrets/
- Repositories: /var/lib/gitea/data/gitea-repositories
- Database (SQLite): /var/lib/gitea/data/gitea.db
- LFS objects: /var/lib/gitea/data/lfs
- Gitea SSH host keys: /var/lib/gitea/data/ssh/
- Logs: journalctl -u gitea
- Service units: /etc/systemd/system/gitea.service, /etc/systemd/system/gitea-firstboot.service
- nginx site: /etc/nginx/sites-available/sac-gitea
- Certificate and key: /etc/nginx/ssl/
- Host name helper: /usr/local/sbin/gitea-set-hostname
- Credentials: /root/gitea-credentials.txt (mode 0600)
Security recommendations
- Allow ports 22, 80, 443 and 2222 in the security group from your own networks only.
- Keep registration off and sign-in required unless you need otherwise. Create accounts for your team as the administrator.
- Keep the administrator password secret, and change it if it may have been shared (Step 5). Consider two-factor authentication (user menu, Settings, Security).
- Leave the outbound request settings as shipped. If a webhook or mirror must reach a host on your private network, allow that one address in
[security] ALLOWED_HOST_LIST(and[migrations] ALLOWED_HOST_LISTfor migrations), never169.254.0.0/16. - Use your own DNS name and a certificate that your browsers and Git clients trust (Step 9).
- Keep the operating system patched. Unattended security upgrades are on, and
sudo apt-get update && sudo apt-get upgradeapplies everything else. - Back up /var/lib/gitea and /etc/gitea regularly (Step 10).
Support and licensing
Gitea is open source under the MIT licence (the licence text is at /usr/share/doc/gitea/LICENSE). There is no per-user or per-CPU licence fee. The AMI price covers SecureAnyCloud's hardening, first-boot setup and support.
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
- Support: 24/7, with a 24-hour response SLA
Deploy on AWS
Launch Gitea 28.2.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com