Docker image
Gitea in Docker. Your own Git, one container.
Gitea is a self-hosted Git service. It gives a team its own place to host repositories, review pull requests, track issues, run built-in Actions CI, and publish releases and packages, all from a single lightweight binary that runs comfortably on a small server.
Prefer not to run it yourself? Managed Gitea from $5/mo
# Gitea in Docker, base pinned by digest docker pull ghcr.io/remarkablecloud/gitea:1.27.3-r1 docker compose up -d
- Current build
1.27.3-r1, updated Sep 10, 2026- Base
- Gitea official, digest-pinned
- What's inside
- Headless setup, SQLite, admin auto-provisioning, healthcheck
- Upstream license
- Gitea: MIT
The image
What our image adds.
What changes from upstream Gitea, and why it matters in production.
Digest-pinned base
The build starts from gitea/gitea@sha256:87a67ee09d3a… (Gitea 1.27.3). Pinning by digest means every rebuild of the 1.27.3-r1 tag resolves to the exact same upstream layer, which is what makes CVE tracking reliable.
First-run admin provisioning
A wrapper entrypoint waits for the app to report healthy, then creates an admin account and prints the generated password once to the container log. You never touch a browser-based installer.
Container healthcheck
A HEALTHCHECK polls /api/healthz, so your orchestrator can gate traffic on readiness and restart the container if Gitea stops responding.
Headless by configuration
The catalog ships the install lock on, public registration off, SQLite as the database, and the public URL derived from the deployment hostname, so the instance is private and correctly linked from the first request.
Provenance labels
Standard OCI labels record the title, description, vendor, and source repository. Everything in Gitea itself (features, storage format, Git internals) is upstream Gitea, unmodified.
Run it yourself, or not
Same image either way.
Pull it and run it anywhere, or let us run Gitea for you on a managed server with updates, monitoring and snapshots.
Run Gitea yourself
- Pull it from GHCR and run it on any Docker host
- The guide below: compose, environment, backups, upgrades
- You patch, back up and watch it
Gitea on a managed server
- Installed for you from this same image, $0 for the app
- Updates with a snapshot first, monitoring and a managed firewall
- A 1 GB server from $5/mo billed annually, with room for more apps
The guide
Run it in production.
Compose file, environment reference, hardening, backups and upgrades, written by the people who build the image.
On this page
Run it with Docker Compose
The image ships with a docker-compose.yml that is deliberately minimal: one
service, one named volume, SQLite inside the volume, and the admin password
generated on first boot.
# RemarkableCloud Gitea - standalone (SQLite; /data volume; auto-provisioned admin).
# docker compose up -d -> http://localhost:3000 (admin password printed in the logs)
# Production runs behind a TLS-terminating proxy; the catalog sets ROOT_URL/DOMAIN from the hostname.
name: gitea
services:
gitea:
image: ${GITEA_IMAGE:-ghcr.io/remarkablecloud/gitea:1.27.3-r1}
restart: unless-stopped
ports: ["${HTTP_PORT:-3000}:3000"]
environment:
GITEA__server__ROOT_URL: ${ROOT_URL:-http://localhost:3000/}
GITEA__server__DOMAIN: ${DOMAIN:-localhost}
GITEA__server__HTTP_PORT: "3000"
GITEA__database__DB_TYPE: sqlite3
GITEA__security__INSTALL_LOCK: "true"
GITEA__service__DISABLE_REGISTRATION: "true"
GITEA_ADMIN_USER: ${GITEA_ADMIN_USER:-admin}
GITEA_ADMIN_PASSWORD: ${GITEA_ADMIN_PASSWORD:-}
GITEA_ADMIN_EMAIL: ${GITEA_ADMIN_EMAIL:-admin@example.com}
volumes:
- gitea-data:/data
volumes:
gitea-data:
Walking through it:
imagepins the RemarkableCloud tag by default and lets you override it withGITEA_IMAGEfor testing a newer build.restart: unless-stoppedbrings Gitea back after a host reboot or crash.portspublishes the container’s port 3000 on the host. In a standalone test that ishttp://localhost:3000. In production you leave this on an internal network and let the proxy reach 3000 directly.environmentlocks the installer, turns off open registration, selects SQLite, and sets the public URL.GITEA_ADMIN_PASSWORDis intentionally empty so the first-run wrapper generates one.volumeskeeps all state (repositories, the SQLite database, config, avatars, LFS) under/datain the namedgitea-datavolume.
Start it and read the generated admin password from the log:
docker compose up -d
docker compose logs gitea | grep '\[rc\]'
You will see a line like [rc] gitea admin 'admin' created; password: .... The
password is printed once. Store it in your password manager, then sign in and
change it or add SSH keys as needed.
Behind Traefik
In production the platform runs Gitea behind Traefik, which terminates TLS. Two settings matter:
- Set
ROOT_URL(andDOMAIN) to your real HTTPS hostname. Gitea usesROOT_URLto build every link and clone URL, so this must match what users type in the browser. - Point the proxy at container port 3000. The healthcheck hits
http://127.0.0.1:3000/api/healthzinside the container, which returns 200 regardless of the request host, so it is never redirected byROOT_URL.
Environment variable reference
HTTP_PORT and GITEA__server__HTTP_PORT are different settings that happen to
share a word: the first is the host port the compose file publishes, the second
is the port Gitea listens on inside the container. Leave the container port at
3000.
| Variable | Compose default | Platform value (catalog) | Notes |
|---|---|---|---|
GITEA_IMAGE | ghcr.io/remarkablecloud/gitea:1.27.3-r1 | pinned by catalog | Image reference; override only to test a build. |
HTTP_PORT | 3000 | n/a (proxy reaches 3000) | Host port published by compose, mapped to container 3000. |
ROOT_URL -> GITEA__server__ROOT_URL | http://localhost:3000/ | https://{hostname}/ | Public base URL. Drives all generated links and clone URLs. |
DOMAIN -> GITEA__server__DOMAIN | localhost | {hostname} | Public hostname. |
GITEA__server__HTTP_PORT | 3000 | 3000 | Port Gitea listens on inside the container. Keep at 3000. |
GITEA__database__DB_TYPE | sqlite3 | sqlite3 | Database backend. This image is shipped SQLite-only. |
GITEA__security__INSTALL_LOCK | true | true | Locks the web installer so Gitea starts headless. |
GITEA__service__DISABLE_REGISTRATION | true | true | Turns off public self-registration. |
GITEA_ADMIN_USER | admin | admin | Username created on first run. |
GITEA_ADMIN_EMAIL | admin@example.com | admin@example.com | Email for the first-run admin. |
GITEA_ADMIN_PASSWORD | empty | not set (generated) | If empty, the wrapper generates a 20-character password and logs it once. |
Gitea reads many more settings through the GITEA__section__KEY convention. Any
value from app.ini can be supplied as an environment variable, so you can add,
for example, SMTP or storage settings the same way.
Hardening notes
- Keep registration closed.
DISABLE_REGISTRATION=truemeans new users are created by an admin. If you deliberately open signups, pair it with an allowlist or email-domain restriction. - Set a strong
ROOT_URLover HTTPS. Terminate TLS at the proxy and never serve Gitea over plain HTTP in production. The image expects a proxy in front. - Publish only the HTTP port. The compose file maps port 3000 only. Gitea
will still display SSH-style clone URLs (
git@host:owner/repo.git), but SSH is not published by this setup, so use the HTTPS clone URLs the web UI shows, or publish and configure SSH deliberately if your team needs it. - This image runs as root internally. It keeps the upstream
gitea/giteaprocess model (an s6 supervisor that drops to thegituser for Gitea itself). That is the supported upstream layout; do not assume a rootless container. - Rotate the first-run password. The generated admin password is written to the container log. Sign in, change it, and consider enabling two-factor authentication on the admin account.
Backups
All state lives in the /data volume: the SQLite database, repositories,
config, LFS objects, and avatars. Backing up that volume backs up the whole
instance.
For a consistent snapshot, use Gitea’s own dump, which packages the database and repositories together:
docker compose exec -u git -w /tmp gitea gitea dump -c /data/gitea/conf/app.ini -f /tmp/gitea-dump.zip
docker compose cp gitea:/tmp/gitea-dump.zip ./gitea-dump.zip
Writing the dump to /tmp keeps it out of /data, which avoids write-permission
issues and stops the dump from bloating later volume backups.
For a simple file-level backup, stop the container and archive the volume so the SQLite database is not written mid-copy:
docker compose stop gitea
docker run --rm -v gitea_gitea-data:/data -v "$PWD":/backup alpine \
tar czf /backup/gitea-data.tgz -C /data .
docker compose start gitea
Test a restore into a throwaway stack before you rely on any backup.
Upgrades
Gitea applies its own database migrations automatically at startup, so upgrading is usually a matter of moving to a newer image tag.
- Read the changelog and the upstream release notes for the target version, watching for breaking changes.
- Back up the
/datavolume first (see above). SQLite migrations are one-way. - Pull the new tag, then recreate the container:
docker compose pull docker compose up -d - Confirm the container reports healthy and check
docker compose logs giteafor migration output.
Move one minor version at a time for large jumps, and never downgrade after a migration has run without restoring from a pre-upgrade backup.
Changelog
Each build is pinned by digest and recorded here, so you can see what changed before you pull a new tag.
-
1.27.3-r1Current Sep 10, 2026 Baseline build. Upstream Gitea 1.27.3.
Gitea and other product names are trademarks of their respective owners. RemarkableCloud is not affiliated with or endorsed by them.
FAQ
Gitea in Docker, answered.
Short answers from the people who build and run this image.
Where is the admin password?
It is generated on first run and printed once to the container log. Run docker compose logs gitea | grep '\[rc\]' right after the first start. To set it yourself, provide GITEA_ADMIN_PASSWORD before the very first boot.
How do I reset a lost admin password?
Use Gitea's own CLI inside the container: docker compose exec -u git gitea gitea admin user change-password --username admin --password 'NEW_PASSWORD'
Can I use MySQL or PostgreSQL instead of SQLite?
This image is shipped for the SQLite workflow (db_mode = none, no external database). SQLite is a good fit for small to medium teams. Moving to an external database is possible with upstream Gitea but is outside what this image is tuned for.
Why can't I clone over SSH?
The compose file publishes only the HTTP port (3000). Gitea still renders git@host clone URLs, but the SSH port is not published by the compose file or the platform. Use the HTTPS clone URL shown in the repository page, or publish and configure SSH yourself.
Does the install wizard ever appear?
No. INSTALL_LOCK=true disables it. All configuration is supplied through environment variables and the /data volume.
Pull it, or let us run it.
The same image runs Gitea on our managed servers, updates and monitoring included.