I bind mount ./data e ./config stavano dentro la cartella code/ che Dokploy ri-clona ad ogni redeploy, cancellando i repository. Passaggio a named volumes (gitea-data, gitea-config) che sopravvivono ai redeploy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Gitea
Self-hosted Gitea behind nginx, backed by MySQL.
internet -> (TLS terminated upstream) -> nginx :80 -> gitea :3000 -> mysql :3306
Three services on two networks: frontend (nginx + gitea) and backend
(gitea + db, internal, so the database has no route out).
Setup
cp .env.example .env
openssl rand -base64 32 # put the result in MYSQL_PASSWORD
./setup.sh
setup.sh creates the bind mounts, fixes their ownership, and brings the stack
up. It refuses to start if MYSQL_PASSWORD is still the placeholder.
The web installer is disabled (GITEA_INSTALL_LOCK=true) because everything it
would ask is already set in .env. Create the first admin from the CLI:
docker compose exec -u git gitea gitea admin user create \
--username admin --email you@example.com --random-password --admin
Registration is closed by default (GITEA_DISABLE_REGISTRATION=true); flip it
in .env if you want open signups.
TLS
The stack terminates nothing: nginx listens on plain :80 and expects TLS to be
handled by whatever sits in front (Cloudflare, a host-level proxy, a load
balancer). That upstream must send X-Forwarded-Proto: https, otherwise Gitea
builds redirects with the wrong scheme. GITEA_PROTOCOL=http with an https://
GITEA_ROOT_URL is correct for this layout.
If you also want the real client IP in Gitea's logs, uncomment the
set_real_ip_from block in nginx/gitea.conf and point it at
the upstream proxy's network.
SSH
Git over SSH runs on the host port in GITEA_SSH_PORT (2222), mapped to port 22
in the container, where the image's own OpenSSH daemon serves it.
GITEA_SSH_PORT only tells Gitea which port to advertise in clone URLs.
Leave GITEA_START_SSH_SERVER=false. Setting it to true makes Gitea's
built-in SSH server try to bind port 22, which sshd already holds, and the
container exits with bind: address already in use.
Backup
# Database
docker compose exec db mysqldump \
-u"$MYSQL_USER" -p"$MYSQL_PASSWORD" --single-transaction "$MYSQL_DATABASE" \
| gzip > gitea-db-$(date +%F).sql.gz
# Repositories, avatars, LFS
tar czf gitea-data-$(date +%F).tar.gz data/
Restore the database with docker compose exec -T db mysql -u... -p... gitea < dump.sql, then restore data/ and start the stack.
Upgrading
GITEA_IMAGE is pinned to a minor series (gitea/gitea:1.26) so docker compose pull picks up patch releases but never jumps a major version, which
would migrate the database irreversibly. Back up before changing the series,
and read the release notes for that jump.