Gitea is a lightweight, self-hosted Git service that’s perfect for personal projects or small teams. Setting it up with HTTPS using a Let’s Encrypt certificate on a local macOS machine can be tricky, especially with Homebrew installations. This guide walks you through a working setup, including running Gitea in the background and handling ACME challenges.
TL:DR – By using a custom LaunchAgent, you can run Gitea in the background with automatic Let’s Encrypt HTTPS, reliable background execution independent of Homebrew’s plist management, optional exposure on standard port 443 via pf redirects and automatic renewal of certificates with minimal fuss. This setup provides a secure, self-hosted Git service suitable for local networks or small teams.
Contents

1. Install Gitea via Homebrew
If you haven’t already installed Gitea:
brew install gitea
Create a working directory and a custom configuration folder:
mkdir -p /opt/homebrew/var/gitea
mkdir -p /opt/homebrew/var/gitea/custom/conf
2. Configure Gitea
Set up your app.ini in:
/opt/homebrew/var/gitea/custom/conf/app.ini
A minimal HTTPS configuration looks like:
[server]
PROTOCOL = https
DOMAIN = git.yourserver.co.uk
HTTP_PORT = 3000
ROOT_URL = https://git.yourserver.co.uk:3000/
ENABLE_ACME = true
ACME_FOLDER = /opt/homebrew/var/gitea/custom/conf/https
ENABLE_ACME = true
allows Gitea to automatically obtain a Let’s Encrypt certificate.- Let’s Encrypt uses HTTP-01 or TLS-ALPN-01 challenges, which require your machine to be reachable externally on port 80 (and 443 for ALPN).
3. Set up environment variables
Gitea needs the GITEA_WORK_DIR
and GITEA_CUSTOM
environment variables set for background execution. Instead of relying on Homebrew’s plist, which is automatically created, and doesn't specify this correctly (for me), you'll need to create a custom LaunchAgent:
vi ~/Library/LaunchAgents/com.yourdomain.gitea.plist
Example plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.yourdomain.gitea</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/opt/gitea/bin/gitea</string>
<string>web</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>GITEA_WORK_DIR</key>
<string>/opt/homebrew/var/gitea</string>
<key>GITEA_CUSTOM</key>
<string>/opt/homebrew/var/gitea/custom</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/opt/homebrew/var/log/gitea.log</string>
<key>StandardErrorPath</key>
<string>/opt/homebrew/var/log/gitea.log</string>
</dict>
</plist>
- This replaces
brew services
, and avoids Homebrew deleting or overwriting plists with ones which won't work should you inadvertently start gitea using Homebrew to manage the service. - Logs are redirected to
/opt/homebrew/var/log/gitea.log
for easy monitoring.
4. Load the custom LaunchAgent
launchctl unload ~/Library/LaunchAgents/com.yourdomain.gitea.plist 2>/dev/null
launchctl load ~/Library/LaunchAgents/com.yourdomain.gitea.plist
Check that Gitea is running:
lsof -iTCP:3000 -sTCP:LISTEN
tail -f /opt/homebrew/var/log/gitea.log
Ensure port 80 is open on your router or firewall while the ACME challenge runs.
5. Optional: redirect HTTPS to standard port 443
Gitea listens internally on port 3000, but you can make it accessible on port 443 using macOS’s packet filter (pf):
- Edit /etc/pf.conf:
rdr pass on lo0 inet proto tcp from any to any port 443 -> 127.0.0.1 port 3000
- Enable and reload:
sudo pfctl -f /etc/pf.conf sudo pfctl -e
- Test:
curl -vk https://git.yourserver.co.uk/
6. Key troubleshooting points
- ACME/Let’s Encrypt failures: Usually caused by port 80 not being reachable externally. Ensure your router/firewall forwards port 80 to your Mac.
- LaunchAgent errors (Input/output error): Often due to incorrect environment variables or inaccessible working directories. Validate plist syntax with:
</liplutil -lint ~/Library/LaunchAgents/com.yourdomain.gitea.plist
- Foreground vs background: Always test Gitea in the foreground first to verify certificates are issued successfully.
- Watch our for gitea processes stopping your loaunch agent from running. Use
ps -ax |grep gitea
to find the process id sndkill -9 processid
to get rid of them.
Summary
By using a custom LaunchAgent, you can run Gitea in the background with:
- Automatic Let’s Encrypt HTTPS
- Reliable background execution independent of Homebrew’s plist management
- Optional exposure on standard port 443 via pf redirects
- Automatic renewal of certificates with minimal fuss
This setup provides a secure, self-hosted Git service suitable for local networks or small teams. See more about what I did with it to make my release management process more robust by Enabling Gitea actions with act_runner on macOS via Homebrew and moving From Local Builds to Production DevOps: Automating Joomla Extension Release Management.