Developer Tools guide
Gitea Docker Compose Setup: Repositories, SSH, and Backups Done Right
Deploy Gitea with durable repository storage, correct UID/GID ownership, HTTP and SSH URLs, a reverse proxy, and tested backups.
Published and reviewed by OpenAlt · September 22, 2026

Table of Contents
- Decide rootful or rootless
- Preflight
- Compose service and persistent data
- Reverse proxy and root URL
- First admin and verification
- Backup, update, and rollback
- Common failures
- Frequently Asked Questions
Decide rootful or rootless
Choose the Gitea image family before deployment and keep it consistent. Gitea’s official Docker installation guide warns that rootful and rootless images are not interchangeable. Switching between them without planning can cause ownership and filesystem problems.
For a small internal team, rootful Compose is generally the simpler starting point because the container, bind mount, and host ownership model are easier to inspect. Rootless may better match a host security policy, but it requires the matching image and consistent permissions.
| Decision | Operational consequence |
|---|---|
| Rootful image | Conventional choice for a standard Docker host |
| Rootless image | Requires the matching rootless image and permissions |
| Bind mount | Keeps repositories and configuration outside the container |
| Named volume | Easier Docker management, but less direct inspection |
Do not combine a rootful image with a rootless data directory or change image families during an update without a documented migration and verified backup.
Preflight
Before starting the service, choose the image family, reserve the hostname, confirm the ports, and decide where /data will live.
- Confirm Docker Compose is available.
- Choose a stable directory for the Gitea bind mount.
- Select host values for the Gitea UID and GID.
- Decide whether users connect directly or through a reverse proxy.
- Reserve HTTP port
3000and SSH host port222. - Choose SQLite or an externally managed database.
- Record the public web URL and SSH clone address.
- Decide where backups will be stored and how restoration will be tested.
The official guide documents the image, /data volume, and default port mappings. Use it as the baseline for your Compose file: read the Docker installation instructions.
Compose service and persistent data
Mount a durable host directory to /data and publish HTTP and SSH with the required mappings.
services:
server:
image: docker.gitea.com/gitea
container_name: gitea
environment:
- USER_UID=${GITEA_UID}
- USER_GID=${GITEA_GID}
restart: always
volumes:
- ./gitea:/data
ports:
- "3000:3000"
- "222:22"
Save this as compose.yaml, create the gitea directory beside it, and set GITEA_UID and GITEA_GID to values that match the intended ownership model. The container should be replaceable without removing repositories or configuration.
UID/GID and /data
Make the host-mounted directory writable by the identity expected by the selected image. If Gitea starts but cannot create repositories, attachments, or configuration files, inspect ownership and permissions before changing application settings.
id
ls -ld ./gitea
docker compose config
Repository storage and configuration must remain on the persistent mount. A running container does not prove that the mount is writable or that updates will preserve data. The Docker documentation explains the storage layout and image assumptions.
HTTP and SSH URLs
The Compose mapping publishes HTTP on host port 3000 and maps host port 222 to the container’s SSH port 22. When users connect directly, SSH clone URLs must include port 222.
ssh://git@your-host.example:222/team/project.git
The hostname is deployment-specific, but the displayed clone URL must match the address users can reach. If SSH is exposed through another external port, configure Gitea’s public SSH values so generated links remain usable.
Reverse proxy and root URL
Set Gitea’s public root URL to the address users actually enter, particularly when a reverse proxy terminates HTTPS. Gitea may listen internally on port 3000 while users access an HTTPS hostname; both layers must describe the same public service.
A typical arrangement is:
- The reverse proxy receives HTTPS traffic for the chosen hostname.
- It forwards requests to Gitea on port
3000. - Gitea’s
ROOT_URLuses the public HTTPS address. - SSH remains reachable through the configured host port.
- Generated web links and clone URLs are checked after login.
If Gitea generates http://localhost, an internal container name, or an SSH URL with the wrong port, correct the root URL and SSH settings before inviting users. Consult the reverse proxy documentation and configuration cheat sheet.


First admin and verification
Create the first administrator through the initial web setup, then test a complete repository workflow. Container health alone does not prove that users can clone or push.
-
Validate the Compose file.
docker compose config -
Start Gitea.
docker compose up -d -
Check status and logs.
docker compose ps docker compose logs --tail=100 server -
Open the configured web URL, complete setup, and create the administrator account.
-
Test HTTP.
curl -I http://your-host.example:3000 -
Test SSH reachability.
ssh -T -p 222 git@your-host.example -
Create a small test repository, clone it using the generated SSH URL, commit a change, and push it.
If the generated URL differs from the address you tested, stop and correct the root URL or SSH configuration. The reverse proxy guidance and configuration reference cover the relevant public-address settings.
Backup, update, and rollback
Back up Gitea before updates and preserve everything needed to reconstruct the deployment. Follow the official backup and restore documentation, especially when using an external database.
Include these items:
- The persistent
/datadirectory, including repositories and configuration. - The database, whether local or separately managed.
- The Compose file and required environment values.
- Reverse-proxy configuration for the public URL.
- The image reference used by the current deployment.
After completing a backup, update with the intended image reference:
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 server
Repeat the HTTP and clone/push tests after the update. For rollback, restore the previous image reference and use the documented restore procedure if database or application changes require it. Do not delete or overwrite /data while troubleshooting; retain the original directory until the replacement has been verified.
Common failures
| Symptom | Likely cause | Corrective check |
|---|---|---|
| Container runs but cannot create data | UID/GID or directory permissions | Inspect id, ownership, and logs |
| Web UI works but SSH clone fails | Port 222 is blocked, unmapped, or misadvertised | Check the mapping and generated URL |
| Clone URL uses the wrong hostname | Incorrect ROOT_URL or SSH settings | Compare the URL with the reachable address |
| Reverse proxy returns an error | Incorrect proxy target or forwarding configuration | Check the destination on port 3000 |
| Data disappears after recreation | /data was not mounted persistently | Inspect the Compose volume definition |
When the cause is unclear, use the official Docker, reverse-proxy, and configuration references rather than assuming a restart repaired storage or URL settings.
Choose the right OpenAlt reference
Inspect Gitea on OpenAlt for the matching project page before deployment. For related self-hosted tools, browse the project-management directory, and use OpenAlt’s difficulty methodology to understand the operational work involved.
Frequently Asked Questions
SQLite or Postgres?
SQLite is simpler for a small internal installation when keeping the database close to Gitea is acceptable. Postgres fits better when your team already operates it separately or wants database administration outside the Gitea container. Include either database in the backup and restore procedure described in Gitea’s official documentation.
Why port 222?
Port 222 avoids competing with the host’s usual SSH service while mapping to Gitea’s container port 22. The mapping is 222:22, and clone URLs must include the externally reachable SSH port.
Can rootful switch to rootless?
Not as a casual image replacement. Rootful and rootless images are not interchangeable. A switch requires planned migration, compatible ownership, and a verified backup. Keep the original /data directory until the new deployment can clone and push successfully.
What belongs in a backup?
Back up persistent /data, the database, Compose and environment configuration, and reverse-proxy settings required to restore the public URL. Follow the backup and restore guide, then test the restored service by accessing repositories and completing a clone/push workflow.
Do I need a reverse proxy?
No. Users can access Gitea directly through host port 3000, provided that the hostname and generated URLs are correct. A reverse proxy is useful when HTTPS or a public hostname is handled outside the container. In that case, set Gitea’s root URL to the public address and forward requests to port 3000.