[init]
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
app-local.ini
|
||||
docker-compose.yml
|
||||
|
||||
165
README.md
Normal file
165
README.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# Gitea on Dokploy
|
||||
|
||||
Self-hosted Gitea deployment running on Dokploy with a local Postgres database.
|
||||
|
||||
- **Domain**: `gitea.routinedevelopment.ca`
|
||||
- **SSH**: Port 2222 (built-in SSH server, HTTP git disabled)
|
||||
- **Database**: Local Postgres 16 (container `gitea-db`)
|
||||
- **Volumes**: `gitea-data` (repos, LFS, avatars, SSH keys), `db-data` (Postgres)
|
||||
|
||||
## Setup (Fresh Install)
|
||||
|
||||
1. Install Dokploy on a clean Ubuntu server:
|
||||
|
||||
```bash
|
||||
curl -sSL https://dokploy.com/install.sh | sh
|
||||
```
|
||||
|
||||
2. Create a new Project in Dokploy called "Gitea".
|
||||
|
||||
3. Create a Docker Compose service inside the project. Paste the contents of `docker-compose.yml`.
|
||||
|
||||
4. Add the environment variable in Dokploy's UI:
|
||||
|
||||
```
|
||||
POSTGRES_PASSWORD=your_strong_password
|
||||
```
|
||||
|
||||
5. Deploy. Let it complete the first boot.
|
||||
|
||||
6. Point your DNS A record for `gitea.routinedevelopment.ca` to the server IP.
|
||||
|
||||
7. In Dokploy's UI, go to the Gitea compose service → **Domains** tab. Add `gitea.routinedevelopment.ca` with container port `3000`, HTTPS enabled, and Let's Encrypt certificate. The docs say Traefik labels alone should work, but in practice you may need to also add the domain via the UI.
|
||||
|
||||
8. Redeploy so Traefik picks up the domain and issues the Let's Encrypt certificate.
|
||||
|
||||
### Domain Troubleshooting
|
||||
|
||||
If the domain doesn't work after deploying:
|
||||
|
||||
- **DNS must resolve first**: Let's Encrypt needs to reach the server. Make sure your DNS/reserved IP points to the server *before* adding the domain in Dokploy. If added too early, recreate the domain or restart Traefik.
|
||||
- **`dokploy-network` is required**: Traefik runs on `dokploy-network`. If your compose service isn't on that network, Traefik can't reach it even if the labels are correct. The compose file in this repo already handles this.
|
||||
- **Reserved IP mismatch**: If using a DigitalOcean reserved IP, Dokploy may complain that the domain resolves to the reserved IP instead of the droplet's direct IP. This is fine — traffic still reaches the server. Deploy anyway.
|
||||
- **Redeploy after any domain change**: Unlike Applications, Docker Compose services require a redeploy for domain changes to take effect.
|
||||
|
||||
## Restore from Backup
|
||||
|
||||
If restoring onto a fresh Dokploy instance after the initial deploy:
|
||||
|
||||
1. SCP the backup files to the server:
|
||||
|
||||
```bash
|
||||
scp gitea_db_backup.dump user@server:~/
|
||||
scp gitea_files_backup.tar.gz user@server:~/
|
||||
scp app.ini user@server:~/ # the REAL one with secrets, not the template
|
||||
```
|
||||
|
||||
2. SSH into the server and run the restore script:
|
||||
|
||||
```bash
|
||||
chmod +x restore.sh
|
||||
./restore.sh
|
||||
```
|
||||
|
||||
3. Verify at `https://gitea.routinedevelopment.ca` — all repos, users, and orgs should be present.
|
||||
|
||||
## Backup
|
||||
|
||||
Run from the Dokploy server:
|
||||
|
||||
```bash
|
||||
chmod +x backup.sh
|
||||
./backup.sh
|
||||
```
|
||||
|
||||
Backups are saved to `~/gitea-backups/` with timestamps. Each backup creates three files:
|
||||
|
||||
- `gitea_db_YYYYMMDD_HHMMSS.dump` — Postgres database dump
|
||||
- `gitea_files_YYYYMMDD_HHMMSS.tar.gz` — Repositories, LFS, avatars, attachments, SSH keys
|
||||
- `app_YYYYMMDD_HHMMSS.ini` — Current app.ini config (contains secrets)
|
||||
|
||||
You can specify a custom backup directory:
|
||||
|
||||
```bash
|
||||
./backup.sh /path/to/backups
|
||||
```
|
||||
|
||||
**Recommended**: Push backups offsite (S3, DigitalOcean Spaces, etc.). Don't rely solely on the same disk.
|
||||
|
||||
## Automated Backups (Optional)
|
||||
|
||||
Add a cron job on the server to run backups daily:
|
||||
|
||||
```bash
|
||||
crontab -e
|
||||
```
|
||||
|
||||
```
|
||||
0 3 * * * /path/to/backup.sh /home/user/gitea-backups >> /var/log/gitea-backup.log 2>&1
|
||||
```
|
||||
|
||||
## Secrets
|
||||
|
||||
The `app.ini` in this repo has `<CHANGE_ME>` placeholders. The real values you need to set:
|
||||
|
||||
| Key | Location | Description |
|
||||
|-----|----------|-------------|
|
||||
| `LFS_JWT_SECRET` | `[server]` | JWT secret for LFS |
|
||||
| `INTERNAL_TOKEN` | `[security]` | Internal API token |
|
||||
| `JWT_SECRET` | `[oauth2]` | OAuth2 JWT secret |
|
||||
| `PASSWD` | `[database]` | Must match `POSTGRES_PASSWORD` env var |
|
||||
|
||||
Keep your real `app.ini` as `app.ini.local` (gitignored) or in a password manager.
|
||||
|
||||
## SSH Remotes
|
||||
|
||||
Since HTTP git is disabled, all clones use SSH on port 2222:
|
||||
|
||||
```bash
|
||||
git clone ssh://git@gitea.routinedevelopment.ca:2222/org/repo.git
|
||||
```
|
||||
|
||||
Note: The container user is `git`, not `gitea`. If migrating from baremetal, update your remotes:
|
||||
|
||||
```bash
|
||||
git remote set-url origin ssh://git@gitea.routinedevelopment.ca:2222/YourOrg/your-repo.git
|
||||
```
|
||||
|
||||
## GitHub Mirror
|
||||
|
||||
To push-mirror a repo to GitHub:
|
||||
|
||||
1. Ensure the container has an SSH key:
|
||||
|
||||
```bash
|
||||
docker exec gitea ls /data/git/.ssh/id_ed25519.pub
|
||||
```
|
||||
|
||||
If not, generate one:
|
||||
|
||||
```bash
|
||||
docker exec gitea ssh-keygen -t ed25519 -f /data/git/.ssh/id_ed25519 -N ""
|
||||
docker exec gitea chown git:git /data/git/.ssh/id_ed25519
|
||||
```
|
||||
|
||||
2. Add the public key as a deploy key (with write access) on your GitHub repo.
|
||||
|
||||
3. Set up a post-receive hook or cron to push.
|
||||
|
||||
The SSH key lives in the `gitea-data` volume and persists across redeploys.
|
||||
|
||||
## Volume Info
|
||||
|
||||
| Volume | Contents | Path in container |
|
||||
|--------|----------|-------------------|
|
||||
| `gitea-data` | Repos, LFS, avatars, config, SSH keys | `/data` |
|
||||
| `db-data` | Postgres data | `/var/lib/postgresql/data` |
|
||||
|
||||
Volumes persist across redeploys. They are only destroyed by `docker compose down -v` or `docker volume rm`. **Never run `down -v` in production.**
|
||||
|
||||
Inspect volumes:
|
||||
|
||||
```bash
|
||||
docker volume ls | grep gitea
|
||||
docker volume inspect <volume_name>
|
||||
```
|
||||
44
docker-compose-example.yml
Normal file
44
docker-compose-example.yml
Normal file
@@ -0,0 +1,44 @@
|
||||
services:
|
||||
gitea:
|
||||
image: gitea/gitea:latest
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
volumes:
|
||||
- gitea-data:/data
|
||||
ports:
|
||||
- "2222:2222"
|
||||
networks:
|
||||
- dokploy-network
|
||||
- internal
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=dokploy-network"
|
||||
- "traefik.http.routers.gitea.rule=Host(`gitea.routinedevelopment.ca`)"
|
||||
- "traefik.http.routers.gitea.entrypoints=websecure"
|
||||
- "traefik.http.routers.gitea.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
|
||||
depends_on:
|
||||
- db
|
||||
restart: unless-stopped
|
||||
|
||||
db:
|
||||
image: postgres:16
|
||||
environment:
|
||||
- POSTGRES_USER=gitea
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
- POSTGRES_DB=gitea
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- internal
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
internal:
|
||||
|
||||
volumes:
|
||||
gitea-data:
|
||||
db-data:
|
||||
92
restore.sh
Normal file
92
restore.sh
Normal file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# =============================================================================
|
||||
# Gitea Restore Script for Dokploy
|
||||
# =============================================================================
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Dokploy is installed and running
|
||||
# - The Gitea compose stack is deployed (first boot completed)
|
||||
# - You have gitea_db_backup.dump and gitea_files_backup.tar.gz in ~/
|
||||
# - Your app.ini (with real secrets) is in ~/
|
||||
#
|
||||
# Usage:
|
||||
# chmod +x restore.sh
|
||||
# ./restore.sh
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
# --- Configuration ---
|
||||
# Update these if your container names differ
|
||||
GITEA_CONTAINER="gitea"
|
||||
DB_CONTAINER="gitea-db"
|
||||
DB_USER="gitea"
|
||||
DB_NAME="gitea"
|
||||
|
||||
BACKUP_DIR="$HOME"
|
||||
DB_DUMP="$BACKUP_DIR/gitea_db_backup.dump"
|
||||
FILES_BACKUP="$BACKUP_DIR/gitea_files_backup.tar.gz"
|
||||
APP_INI="$BACKUP_DIR/app.ini"
|
||||
|
||||
# --- Preflight checks ---
|
||||
echo "=== Preflight checks ==="
|
||||
|
||||
for file in "$DB_DUMP" "$FILES_BACKUP" "$APP_INI"; do
|
||||
if [[ ! -f "$file" ]]; then
|
||||
echo "ERROR: $file not found"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if ! docker ps --format '{{.Names}}' | grep -q "^${DB_CONTAINER}$"; then
|
||||
echo "ERROR: Container $DB_CONTAINER is not running"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! docker ps --format '{{.Names}}' | grep -q "^${GITEA_CONTAINER}$"; then
|
||||
echo "ERROR: Container $GITEA_CONTAINER is not running"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "All checks passed."
|
||||
|
||||
# --- Restore database ---
|
||||
echo ""
|
||||
echo "=== Restoring database ==="
|
||||
docker cp "$DB_DUMP" "$DB_CONTAINER":/tmp/gitea_db_backup.dump
|
||||
docker exec -it "$DB_CONTAINER" pg_restore \
|
||||
-U "$DB_USER" -d "$DB_NAME" \
|
||||
--no-owner --no-acl \
|
||||
/tmp/gitea_db_backup.dump
|
||||
echo "Database restored."
|
||||
|
||||
# --- Restore files ---
|
||||
echo ""
|
||||
echo "=== Restoring files ==="
|
||||
docker cp "$FILES_BACKUP" "$GITEA_CONTAINER":/tmp/gitea_files_backup.tar.gz
|
||||
docker exec -it "$GITEA_CONTAINER" bash -c '
|
||||
cd /tmp
|
||||
tar xzf gitea_files_backup.tar.gz --strip-components=3
|
||||
cp -r repositories /data/git/
|
||||
[ -d lfs ] && cp -r lfs /data/git/
|
||||
[ -d avatars ] && cp -r avatars /data/gitea/
|
||||
[ -d attachments ] && cp -r attachments /data/gitea/
|
||||
chown -R git:git /data/git /data/gitea
|
||||
rm -rf /tmp/repositories /tmp/lfs /tmp/avatars /tmp/attachments /tmp/gitea_files_backup.tar.gz
|
||||
'
|
||||
echo "Files restored."
|
||||
|
||||
# --- Inject app.ini ---
|
||||
echo ""
|
||||
echo "=== Injecting app.ini ==="
|
||||
docker cp "$APP_INI" "$GITEA_CONTAINER":/data/gitea/conf/app.ini
|
||||
echo "Config injected."
|
||||
|
||||
# --- Restart ---
|
||||
echo ""
|
||||
echo "=== Restarting Gitea ==="
|
||||
docker restart "$GITEA_CONTAINER"
|
||||
echo "Done! Gitea should be available shortly."
|
||||
echo ""
|
||||
echo "Verify with: curl -s http://\$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $GITEA_CONTAINER):3000 | head -5"
|
||||
Reference in New Issue
Block a user