Apps / Docker Images
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.
docker pull ghcr.io/remarkablecloud/gitea 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
Free to pull and run anywhere. Digest-pinned, updated deliberately; each build is recorded in the changelog.
The image
What our image adds.
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.
Digest-pinned base
The build starts from `gitea/gitea@sha256:87a67ee09d3ae0d1df5fda5dcda3e2a1f9236a45b0a59025d6e00e46adc43bef` (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
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.
Rather skip the compose file? Deploy Gitea with one click on your own VPS: TLS, domain and database already wired.
Deploy Gitea in one clickThe guide
Run it in production.
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.
Updates
Recent builds.
1.27.3-r1
Sep 10, 2026Baseline build. Upstream Gitea 1.27.3. Pinned base image: ``` FROM gitea/gitea@sha256:87a67ee09d3ae0d1df5fda5dcda3e2a1f9236a45b0a59025d6e00e46adc43bef ```
Or skip the ops
Host Gitea on your own private VPS.
One click installs this exact image on a VPS that is yours alone, domain and TLS wizard included. The same server runs as many apps as fit, with no per-app charge.
FAQ
Questions we get.
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.
Your server runs. You sleep.
Fully managed hosting from people who have been doing this since 2001.