From ae60b924021d40eba16ea0f29c8f17415f03fe33 Mon Sep 17 00:00:00 2001 From: Vladimir Mikhalev Date: Fri, 1 Sep 2023 18:31:13 -0400 Subject: [PATCH] Gitea with Let's Encrypt Using Docker Compose --- .env | 33 +++ .github/FUNDING.yml | 1 + .gitignore | 284 +++++++++++++++++++ .infragenie/infrastructure_model.png | Bin 78115 -> 0 bytes README.md | 82 +++++- gitea-restore-application-data.sh | 29 +- gitea-restore-database.sh | 32 ++- gitea-traefik-letsencrypt-docker-compose.yml | 212 ++++++++------ 8 files changed, 565 insertions(+), 108 deletions(-) create mode 100644 .env create mode 100644 .gitignore delete mode 100644 .infragenie/infrastructure_model.png diff --git a/.env b/.env new file mode 100644 index 0000000..3ce974a --- /dev/null +++ b/.env @@ -0,0 +1,33 @@ +# Traefik Variables +TRAEFIK_IMAGE_TAG=traefik:2.9 +TRAEFIK_LOG_LEVEL=WARN +TRAEFIK_ACME_EMAIL=callvaldemar@gmail.com +TRAEFIK_HOSTNAME=traefik.gitea.heyvaldemar.net +# Basic Authentication for Traefik Dashboard +# Username: traefikadmin +# Passwords must be encoded using MD5, SHA1, or BCrypt https://hostingcanada.org/htpasswd-generator/ +TRAEFIK_BASIC_AUTH=traefikadmin:$$2y$$10$$sMzJfirKC75x/hVpiINeZOiSm.Jkity9cn4KwNkRvO7hSQVFc5FLO + +# Gitea Variables +GITEA_POSTGRES_IMAGE_TAG=postgres:15 +GITEA_IMAGE_TAG=bitnami/gitea:1.20.3 +GITEA_DB_NAME=giteadb +GITEA_DB_USER=giteadbuser +GITEA_DB_PASSWORD=etFneCEtAWRKkfeQmkvwLWE +GITEA_ADMIN_USERNAME=giteaadmin +GITEA_ADMIN_PASSWORD=XfyEVC4uJLyXnrjDtQGk +GITEA_ADMIN_EMAIL=giteaadmin@heyvaldemar.net +GITEA_URL=https://gitea.heyvaldemar.net +GITEA_HOSTNAME=gitea.heyvaldemar.net +GITEA_SHELL_SSH_PORT=2222 + +# Backup Variables +BACKUP_INIT_SLEEP=30m +BACKUP_INTERVAL=24h +POSTGRES_BACKUP_PRUNE_DAYS=7 +DATA_BACKUP_PRUNE_DAYS=7 +POSTGRES_BACKUPS_PATH=/srv/gitea-postgres/backups +DATA_BACKUPS_PATH=/srv/gitea-application-data/backups +DATA_PATH=/bitnami/gitea +POSTGRES_BACKUP_NAME=gitea-postgres-backup +DATA_BACKUP_NAME=gitea-application-data-backup diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 537f667..89b098c 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,4 @@ github: heyvaldemar patreon: heyvaldemar ko_fi: heyvaldemar +custom: ['paypal.com/paypalme/heyValdemarCOM', 'buymeacoffee.com/heyValdemar', 'ko-fi.com/heyValdemar'] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1c63c60 --- /dev/null +++ b/.gitignore @@ -0,0 +1,284 @@ +# Created by https://www.toptal.com/developers/gitignore/api/git,macos,xcode,jekyll,packer,ansible,vagrant,windows,notepadpp,terraform,powershell,terragrunt,sublimetext,ansibletower,visualstudiocode,linux +# Edit at https://www.toptal.com/developers/gitignore?templates=git,macos,xcode,jekyll,packer,ansible,vagrant,windows,notepadpp,terraform,powershell,terragrunt,sublimetext,ansibletower,visualstudiocode,linux + +### Ansible ### +*.retry + +### AnsibleTower ### +# Ansible runtime and backups +*.original +*.tmp +*.bkp +*.*~ + +# Tower runtime roles +roles/** +!roles/requirements.yml + +# Avoid plain-text passwords +*pwd* +*pass* +*password* +*.txt + +# Exclude all binaries +*.bin +*.jar +*.tar +*.zip +*.gzip +*.tgz + + +### Git ### +# Created by git for backups. To disable backups in Git: +# $ git config --global mergetool.keepBackup false +*.orig + +# Created by git when using merge tools for conflicts +*.BACKUP.* +*.BASE.* +*.LOCAL.* +*.REMOTE.* +*_BACKUP_*.txt +*_BASE_*.txt +*_LOCAL_*.txt +*_REMOTE_*.txt + +### Jekyll ### +_site/ +.sass-cache/ +.jekyll-cache/ +.jekyll-metadata +# Ignore folders generated by Bundler +.bundle/ +vendor/ + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### NotepadPP ### +# Notepad++ backups # +*.bak + +### Packer ### +# Cache objects +packer_cache/ + +# Crash log +crash.log + +# https://www.packer.io/guides/hcl/variables +# Exclude all .pkrvars.hcl files, which are likely to contain sensitive data, +# such as password, private keys, and other secrets. These should not be part of +# version control as they are data points which are potentially sensitive and +# subject to change depending on the environment. +# +*.pkrvars.hcl + +# For built boxes +*.box + +### Packer Patch ### +# ignore temporary output files +output-*/ + +### PowerShell ### +# Exclude packaged modules + +# Exclude .NET assemblies from source +*.dll + +### SublimeText ### +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json +sftp-config-alt*.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings + +### Terraform ### +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.*.log + +# Exclude all .tfvars files, which are likely to contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. +*.tfvars +*.tfvars.json + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + +# Ignore CLI configuration files +.terraformrc +terraform.rc + +### Terragrunt ### +# terragrunt cache directories +**/.terragrunt-cache/* + +# Terragrunt debug output file (when using `--terragrunt-debug` option) +# See: https://terragrunt.gruntwork.io/docs/reference/cli-options/#terragrunt-debug +terragrunt-debug.tfvars.json + +### Vagrant ### +# General +.vagrant/ + +# Log files (if you are creating logs in debug mode, uncomment this) +# *.log + +### Vagrant Patch ### + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Xcode ### +## User settings +xcuserdata/ + +## Xcode 8 and earlier +*.xcscmblueprint +*.xccheckout + +### Xcode Patch ### +*.xcodeproj/* +!*.xcodeproj/project.pbxproj +!*.xcodeproj/xcshareddata/ +!*.xcodeproj/project.xcworkspace/ +!*.xcworkspace/contents.xcworkspacedata +/*.gcno +**/xcshareddata/WorkspaceSettings.xcsettings + +# End of https://www.toptal.com/developers/gitignore/api/git,macos,xcode,jekyll,packer,ansible,vagrant,windows,notepadpp,terraform,powershell,terragrunt,sublimetext,ansibletower,visualstudiocode,linux \ No newline at end of file diff --git a/.infragenie/infrastructure_model.png b/.infragenie/infrastructure_model.png deleted file mode 100644 index 3cb310bdacdbbdc4da07306e02e1ed200b8d2ce5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78115 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 D$9A=+ diff --git a/README.md b/README.md index e6f1f95..f40b8fe 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,101 @@ -# Gitea with Let's Encrypt in a Docker Compose +# Gitea with Let's Encrypt Using Docker Compose -Install Docker Engine and Docker Compose by following my [guide](https://www.heyvaldemar.com/install-docker-engine-and-docker-compose-on-ubuntu-server/). +๐Ÿ“™ The complete installation guide is available on my [website](https://www.heyvaldemar.com/install-gitea-using-docker-compose/). -Run `gitea-restore-application-data.sh` to restore application data if needed. +โ— Change variables in the `.env` to meet your requirements. -Run `gitea-restore-database.sh` to restore database if needed. +๐Ÿ’ก Note that the .env file should be in the same directory as `gitea-traefik-letsencrypt-docker-compose.yml`. -Deploy Gitea server with a Docker Compose using the command: +Create networks for your services before deploying the configuration using the commands: + +`docker network create traefik-network` + +`docker network create gitea-network` + +Deploy Gitea using Docker Compose: `docker compose -f gitea-traefik-letsencrypt-docker-compose.yml -p gitea up -d` -# Infrastructure Model -![Infrastructure model](.infragenie/infrastructure_model.png) +# Backups + +The `backups` container in the configuration is responsible for the following: + +1. **Database Backup**: Creates compressed backups of the PostgreSQL database using pg_dump. +Customizable backup path, filename pattern, and schedule through variables like `POSTGRES_BACKUPS_PATH`, `POSTGRES_BACKUP_NAME`, and `BACKUP_INTERVAL`. + +2. **Application Data Backup**: Compresses and stores backups of the application data on the same schedule. Controlled via variables such as `DATA_BACKUPS_PATH`, `DATA_BACKUP_NAME`, and `BACKUP_INTERVAL`. + +3. **Backup Pruning**: Periodically removes backups exceeding a specified age to manage storage. Customizable pruning schedule and age threshold with `POSTGRES_BACKUP_PRUNE_DAYS` and `DATA_BACKUP_PRUNE_DAYS`. + +By utilizing this container, consistent and automated backups of the essential components of your instance are ensured. Moreover, efficient management of backup storage and tailored backup routines can be achieved through easy and flexible configuration using environment variables. + +# gitea-restore-database.sh Description + +This script facilitates the restoration of a database backup: + +1. **Identify Containers**: It first identifies the service and backups containers by name, finding the appropriate container IDs. + +2. **List Backups**: Displays all available database backups located at the specified backup path. + +3. **Select Backup**: Prompts the user to copy and paste the desired backup name from the list to restore the database. + +4. **Stop Service**: Temporarily stops the service to ensure data consistency during restoration. + +5. **Restore Database**: Executes a sequence of commands to drop the current database, create a new one, and restore it from the selected compressed backup file. + +6. **Start Service**: Restarts the service after the restoration is completed. + +To make the `gitea-restore-database.shh` script executable, run the following command: + +`chmod +x gitea-restore-database.sh` + +Usage of this script ensures a controlled and guided process to restore the database from an existing backup. + +# gitea-restore-application-data.sh Description + +This script is designed to restore the application data: + +1. **Identify Containers**: Similarly to the database restore script, it identifies the service and backups containers by name. + +2. **List Application Data Backups**: Displays all available application data backups at the specified backup path. + +3. **Select Backup**: Asks the user to copy and paste the desired backup name for application data restoration. + +4. **Stop Service**: Stops the service to prevent any conflicts during the restore process. + +5. **Restore Application Data**: Removes the current application data and then extracts the selected backup to the appropriate application data path. + +6. **Start Service**: Restarts the service after the application data has been successfully restored. + +To make the `gitea-restore-application-data.sh` script executable, run the following command: + +`chmod +x gitea-restore-application-data.sh` + +By utilizing this script, you can efficiently restore application data from an existing backup while ensuring proper coordination with the running service. # Author -hey, Iโ€™m Vladimir Mikhalev, but my friends call me Valdemar. + +Iโ€™m Vladimir Mikhalev, the [Docker Captain](https://www.docker.com/captains/vladimir-mikhalev/), but my friends can call me Valdemar. ๐ŸŒ My [website](https://www.heyvaldemar.com/) with detailed IT guides\ ๐ŸŽฌ Follow me on [YouTube](https://www.youtube.com/channel/UCf85kQ0u1sYTTTyKVpxrlyQ?sub_confirmation=1)\ ๐Ÿฆ Follow me on [Twitter](https://twitter.com/heyValdemar)\ ๐ŸŽจ Follow me on [Instagram](https://www.instagram.com/heyvaldemar/)\ +๐Ÿงต Follow me on [Threads](https://www.threads.net/@heyvaldemar)\ +๐Ÿ˜ Follow me on [Mastodon](https://hachyderm.io/@heyValdemar)\ +๐ŸงŠ Follow me on [Bluesky](https://bsky.app/profile/heyvaldemar.bsky.social)\ ๐ŸŽธ Follow me on [Facebook](https://www.facebook.com/heyValdemarFB/)\ ๐ŸŽฅ Follow me on [TikTok](https://www.tiktok.com/@heyvaldemar)\ ๐Ÿ’ป Follow me on [LinkedIn](https://www.linkedin.com/in/heyvaldemar/)\ ๐Ÿˆ Follow me on [GitHub](https://github.com/heyvaldemar) # Communication + ๐Ÿ‘พ Chat with IT pros on [Discord](https://discord.gg/AJQGCCBcqf)\ ๐Ÿ“ง Reach me at ask@sre.gg # Give Thanks + ๐Ÿ’Ž Support on [GitHub](https://github.com/sponsors/heyValdemar)\ ๐Ÿ† Support on [Patreon](https://www.patreon.com/heyValdemar)\ ๐Ÿฅค Support on [BuyMeaCoffee](https://www.buymeacoffee.com/heyValdemar)\ diff --git a/gitea-restore-application-data.sh b/gitea-restore-application-data.sh index 918ecb3..58757aa 100644 --- a/gitea-restore-application-data.sh +++ b/gitea-restore-application-data.sh @@ -1,17 +1,32 @@ #!/bin/bash -GITEA_CONTAINER=$(docker ps -aqf "name=gitea_gitea") -GITEA_BACKUPS_CONTAINER=$(docker ps -aqf "name=gitea_backups") +# # gitea-restore-application-data.sh Description +# This script is designed to restore the application data. +# 1. **Identify Containers**: Similarly to the database restore script, it identifies the service and backups containers by name. +# 2. **List Application Data Backups**: Displays all available application data backups at the specified backup path. +# 3. **Select Backup**: Asks the user to copy and paste the desired backup name for application data restoration. +# 4. **Stop Service**: Stops the service to prevent any conflicts during the restore process. +# 5. **Restore Application Data**: Removes the current application data and then extracts the selected backup to the appropriate application data path. +# 6. **Start Service**: Restarts the service after the application data has been successfully restored. +# To make the `gitea-restore-application-data.sh` script executable, run the following command: +# `chmod +x gitea-restore-application-data.sh` +# By utilizing this script, you can efficiently restore application data from an existing backup while ensuring proper coordination with the running service. + +GITEA_CONTAINER=$(docker ps -aqf "name=gitea-gitea") +GITEA_BACKUPS_CONTAINER=$(docker ps -aqf "name=gitea-backups") +BACKUP_PATH="/srv/gitea-application-data/backups/" +RESTORE_PATH="/gitea/data/" +BACKUP_PREFIX="gitea-application-data" echo "--> All available application data backups:" -for entry in $(docker container exec -it $GITEA_BACKUPS_CONTAINER sh -c "ls /srv/gitea-application-data/backups/") +for entry in $(docker container exec -it "$GITEA_BACKUPS_CONTAINER" sh -c "ls $BACKUP_PATH") do echo "$entry" done echo "--> Copy and paste the backup name from the list above to restore application data and press [ENTER] ---> Example: gitea-application-data-backup-YYYY-MM-DD_hh-mm.tar.gz" +--> Example: ${BACKUP_PREFIX}-backup-YYYY-MM-DD_hh-mm.tar.gz" echo -n "--> " read SELECTED_APPLICATION_BACKUP @@ -19,11 +34,11 @@ read SELECTED_APPLICATION_BACKUP echo "--> $SELECTED_APPLICATION_BACKUP was selected" echo "--> Stopping service..." -docker stop $GITEA_CONTAINER +docker stop "$GITEA_CONTAINER" echo "--> Restoring application data..." -docker exec -it $GITEA_BACKUPS_CONTAINER sh -c "rm -rf /etc/gitea/* && tar -zxpf /srv/gitea-application-data/backups/$SELECTED_APPLICATION_BACKUP -C /" +docker exec -it "$GITEA_BACKUPS_CONTAINER" sh -c "rm -rf ${RESTORE_PATH}* && tar -zxpf ${BACKUP_PATH}${SELECTED_APPLICATION_BACKUP} -C /" echo "--> Application data recovery completed..." echo "--> Starting service..." -docker start $GITEA_CONTAINER +docker start "$GITEA_CONTAINER" diff --git a/gitea-restore-database.sh b/gitea-restore-database.sh index 421142b..e8e1897 100644 --- a/gitea-restore-database.sh +++ b/gitea-restore-database.sh @@ -1,11 +1,27 @@ #!/bin/bash -GITEA_CONTAINER=$(docker ps -aqf "name=gitea_gitea") -GITEA_BACKUPS_CONTAINER=$(docker ps -aqf "name=gitea_backups") +# # gitea-restore-database.sh Description +# This script facilitates the restoration of a database backup. +# 1. **Identify Containers**: It first identifies the service and backups containers by name, finding the appropriate container IDs. +# 2. **List Backups**: Displays all available database backups located at the specified backup path. +# 3. **Select Backup**: Prompts the user to copy and paste the desired backup name from the list to restore the database. +# 4. **Stop Service**: Temporarily stops the service to ensure data consistency during restoration. +# 5. **Restore Database**: Executes a sequence of commands to drop the current database, create a new one, and restore it from the selected compressed backup file. +# 6. **Start Service**: Restarts the service after the restoration is completed. +# To make the `gitea-restore-database.shh` script executable, run the following command: +# `chmod +x gitea-restore-database.sh` +# Usage of this script ensures a controlled and guided process to restore the database from an existing backup. + +GITEA_CONTAINER=$(docker ps -aqf "name=gitea-gitea") +GITEA_BACKUPS_CONTAINER=$(docker ps -aqf "name=gitea-backups") +GITEA_DB_NAME="giteadb" +GITEA_DB_USER="giteadbuser" +POSTGRES_PASSWORD=$(docker exec $GITEA_BACKUPS_CONTAINER printenv PGPASSWORD) +BACKUP_PATH="/srv/gitea-postgres/backups/" echo "--> All available database backups:" -for entry in $(docker container exec -it $GITEA_BACKUPS_CONTAINER sh -c "ls /srv/gitea-postgres/backups/") +for entry in $(docker container exec "$GITEA_BACKUPS_CONTAINER" sh -c "ls $BACKUP_PATH") do echo "$entry" done @@ -19,13 +35,13 @@ read SELECTED_DATABASE_BACKUP echo "--> $SELECTED_DATABASE_BACKUP was selected" echo "--> Stopping service..." -docker stop $GITEA_CONTAINER +docker stop "$GITEA_CONTAINER" echo "--> Restoring database..." -docker exec -it $GITEA_BACKUPS_CONTAINER sh -c 'PGPASSWORD="$(echo $POSTGRES_PASSWORD)" dropdb -h postgres -p 5432 giteadb -U giteadbuser \ -&& PGPASSWORD="$(echo $POSTGRES_PASSWORD)" createdb -h postgres -p 5432 giteadb -U giteadbuser \ -&& PGPASSWORD="$(echo $POSTGRES_PASSWORD)" gunzip -c /srv/gitea-postgres/backups/'$SELECTED_DATABASE_BACKUP' | PGPASSWORD=$(echo $POSTGRES_PASSWORD) psql -h postgres -p 5432 giteadb -U giteadbuser' +docker exec "$GITEA_BACKUPS_CONTAINER" sh -c "dropdb -h postgres -p 5432 $GITEA_DB_NAME -U $GITEA_DB_USER \ +&& createdb -h postgres -p 5432 $GITEA_DB_NAME -U $GITEA_DB_USER \ +&& gunzip -c ${BACKUP_PATH}${SELECTED_DATABASE_BACKUP} | psql -h postgres -p 5432 $GITEA_DB_NAME -U $GITEA_DB_USER" echo "--> Database recovery completed..." echo "--> Starting service..." -docker start $GITEA_CONTAINER +docker start "$GITEA_CONTAINER" diff --git a/gitea-traefik-letsencrypt-docker-compose.yml b/gitea-traefik-letsencrypt-docker-compose.yml index b07ec8b..9ec8420 100644 --- a/gitea-traefik-letsencrypt-docker-compose.yml +++ b/gitea-traefik-letsencrypt-docker-compose.yml @@ -1,12 +1,62 @@ -# Gitea with Let's Encrypt in a Docker Compose +# Gitea with Let's Encrypt Using Docker Compose + +# The complete installation guide is available on my https://www.heyvaldemar.com/install-gitea-using-docker-compose/ + +# Change variables in the `.env` to meet your requirements. +# Note that the .env file should be in the same directory as `gitea-traefik-letsencrypt-docker-compose.yml`. + +# Create networks for your services before deploying the configuration using the commands: +# `docker network create traefik-network` +# `docker network create gitea-network` + +# Deploy Gitea using Docker Compose: +# `docker compose -f gitea-traefik-letsencrypt-docker-compose.yml -p gitea up -d` + +# Backups +# The `backups` container in the configuration is responsible for the following: +# 1. **Database Backup**: Creates compressed backups of the PostgreSQL database using pg_dump. +# Customizable backup path, filename pattern, and schedule through variables like `POSTGRES_BACKUPS_PATH`, `POSTGRES_BACKUP_NAME`, and `BACKUP_INTERVAL`. +# 2. **Application Data Backup**: Compresses and stores backups of the application data on the same schedule. Controlled via variables such as `DATA_BACKUPS_PATH`, `DATA_BACKUP_NAME`, and `BACKUP_INTERVAL`. +# 3. **Backup Pruning**: Periodically removes backups exceeding a specified age to manage storage. +# Customizable pruning schedule and age threshold with `POSTGRES_BACKUP_PRUNE_DAYS` and `DATA_BACKUP_PRUNE_DAYS`. +# By utilizing this container, consistent and automated backups of the essential components of your instance are ensured. +# Moreover, efficient management of backup storage and tailored backup routines can be achieved through easy and flexible configuration using environment variables. + +# # gitea-restore-database.sh Description +# This script facilitates the restoration of a database backup. +# 1. **Identify Containers**: It first identifies the service and backups containers by name, finding the appropriate container IDs. +# 2. **List Backups**: Displays all available database backups located at the specified backup path. +# 3. **Select Backup**: Prompts the user to copy and paste the desired backup name from the list to restore the database. +# 4. **Stop Service**: Temporarily stops the service to ensure data consistency during restoration. +# 5. **Restore Database**: Executes a sequence of commands to drop the current database, create a new one, and restore it from the selected compressed backup file. +# 6. **Start Service**: Restarts the service after the restoration is completed. +# To make the `gitea-restore-database.shh` script executable, run the following command: +# `chmod +x gitea-restore-database.sh` +# Usage of this script ensures a controlled and guided process to restore the database from an existing backup. + +# # gitea-restore-application-data.sh Description +# This script is designed to restore the application data. +# 1. **Identify Containers**: Similarly to the database restore script, it identifies the service and backups containers by name. +# 2. **List Application Data Backups**: Displays all available application data backups at the specified backup path. +# 3. **Select Backup**: Asks the user to copy and paste the desired backup name for application data restoration. +# 4. **Stop Service**: Stops the service to prevent any conflicts during the restore process. +# 5. **Restore Application Data**: Removes the current application data and then extracts the selected backup to the appropriate application data path. +# 6. **Start Service**: Restarts the service after the application data has been successfully restored. +# To make the `gitea-restore-application-data.sh` script executable, run the following command: +# `chmod +x gitea-restore-application-data.sh` +# By utilizing this script, you can efficiently restore application data from an existing backup while ensuring proper coordination with the running service. # Author -# hey, Iโ€™m Vladimir Mikhalev, but my friends call me Valdemar. +# Iโ€™m Vladimir Mikhalev, the Docker Captain, but my friends can call me Valdemar. +# https://www.docker.com/captains/vladimir-mikhalev/ # My website with detailed IT guides: https://www.heyvaldemar.com/ # Follow me on YouTube: https://www.youtube.com/channel/UCf85kQ0u1sYTTTyKVpxrlyQ?sub_confirmation=1 # Follow me on Twitter: https://twitter.com/heyValdemar # Follow me on Instagram: https://www.instagram.com/heyvaldemar/ +# Follow me on Threads: https://www.threads.net/@heyvaldemar +# Follow me on Mastodon: https://hachyderm.io/@heyValdemar +# Follow me on Bluesky: https://bsky.app/profile/heyvaldemar.bsky.social # Follow me on Facebook: https://www.facebook.com/heyValdemarFB/ # Follow me on TikTok: https://www.tiktok.com/@heyvaldemar # Follow me on LinkedIn: https://www.linkedin.com/in/heyvaldemar/ @@ -23,37 +73,33 @@ # Support on Ko-fi: https://ko-fi.com/heyValdemar # Support on PayPal: https://www.paypal.com/paypalme/heyValdemarCOM -# Install Docker Engine and Docker Compose by following my guide: https://www.heyvaldemar.com/install-docker-engine-and-docker-compose-on-ubuntu-server/ - -# Run gitea-restore-application-data.sh to restore application data if needed. -# Run gitea-restore-database.sh to restore database if needed. - -# Deploy Gitea server with a Docker Compose using the command: -# docker compose -f gitea-traefik-letsencrypt-docker-compose.yml -p gitea up -d +networks: + gitea-network: + external: true + traefik-network: + external: true volumes: gitea-data: - gitea-config: gitea-postgres: + gitea-postgres-backup: gitea-data-backups: - gitea-postgres-backups: + gitea-database-backups: traefik-certificates: services: postgres: - # Image tag (replace with yours) - image: postgres:14 + image: ${GITEA_POSTGRES_IMAGE_TAG} volumes: - gitea-postgres:/var/lib/postgresql/data environment: - # Database name (replace with yours) - POSTGRES_DB: giteadb - # Database user (replace with yours) - POSTGRES_USER: giteadbuser - # Database password (replace with yours) - POSTGRES_PASSWORD: etFneCEtAWRKkfeQmkvwLWE + POSTGRES_DB: ${GITEA_DB_NAME} + POSTGRES_USER: ${GITEA_DB_USER} + POSTGRES_PASSWORD: ${GITEA_DB_PASSWORD} + networks: + - gitea-network healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres -h 127.0.0.1"] + test: [ "CMD", "pg_isready", "-q", "-d", "${GITEA_DB_NAME}", "-U", "${GITEA_DB_USER}" ] interval: 10s timeout: 5s retries: 3 @@ -61,28 +107,29 @@ services: restart: unless-stopped gitea: - # Image tag (replace with yours) - image: gitea/gitea:1.17 + image: ${GITEA_IMAGE_TAG} volumes: - - gitea-data:/data - - gitea-config:/etc/gitea + - gitea-data:/${DATA_PATH} - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro environment: - DB_TYPE: postgres - DB_HOST: postgres:5432 - # Database name (replace with yours) - DB_NAME: giteadb - # Database user (replace with yours) - DB_USER: giteadbuser - # Database password (replace with yours) - DB_PASSWD: etFneCEtAWRKkfeQmkvwLWE - RUN_MODE: prod - SSH_PORT: 0 - DISABLE_SSH: 'true' - HTTP_PORT: 3000 - # Gitea URL (replace with yours) - ROOT_URL: https://gitea.heyvaldemar.net + GITEA_DATABASE_HOST: postgres + GITEA_DATABASE_NAME: ${GITEA_DB_NAME} + GITEA_DATABASE_USERNAME: ${GITEA_DB_USER} + GITEA_DATABASE_PASSWORD: ${GITEA_DB_PASSWORD} + GITEA_ADMIN_USER: ${GITEA_ADMIN_USERNAME} + GITEA_ADMIN_PASSWORD: ${GITEA_ADMIN_PASSWORD} + GITEA_ADMIN_EMAIL: ${GITEA_ADMIN_EMAIL} + GITEA_RUN_MODE: prod + GITEA_DOMAIN: ${GITEA_HOSTNAME} + GITEA_SSH_DOMAIN: ${GITEA_HOSTNAME} + GITEA_ROOT_URL: ${GITEA_URL} + GITEA_HTTP_PORT: 3000 + GITEA_SSH_PORT: ${GITEA_SHELL_SSH_PORT} + GITEA_SSH_LISTEN_PORT: 22 + networks: + - gitea-network + - traefik-network healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/"] interval: 10s @@ -91,8 +138,7 @@ services: start_period: 90s labels: - "traefik.enable=true" - # Gitea URL (replace with yours) - - "traefik.http.routers.gitea.rule=Host(`gitea.heyvaldemar.net`)" + - "traefik.http.routers.gitea.rule=Host(`${GITEA_HOSTNAME}`)" - "traefik.http.routers.gitea.service=gitea" - "traefik.http.routers.gitea.entrypoints=websecure" - "traefik.http.services.gitea.loadbalancer.server.port=3000" @@ -101,6 +147,11 @@ services: - "traefik.http.services.gitea.loadbalancer.passhostheader=true" - "traefik.http.routers.gitea.middlewares=compresstraefik" - "traefik.http.middlewares.compresstraefik.compress=true" + - "traefik.tcp.routers.gitea-ssh.rule=HostSNI(`*`)" + - "traefik.tcp.routers.gitea-ssh.service=gitea-ssh" + - "traefik.tcp.routers.gitea-ssh.entrypoints=ssh" + - "traefik.tcp.services.gitea-ssh.loadbalancer.server.port=22" + - "traefik.docker.network=traefik-network" restart: unless-stopped depends_on: postgres: @@ -109,10 +160,9 @@ services: condition: service_healthy traefik: - # Image tag (replace with yours) - image: traefik:2.8 + image: ${TRAEFIK_IMAGE_TAG} command: - - "--log.level=WARN" + - "--log.level=${TRAEFIK_LOG_LEVEL}" - "--accesslog=true" - "--api.dashboard=true" - "--api.insecure=true" @@ -121,12 +171,12 @@ services: - "--entryPoints.ping.address=:8082" - "--entryPoints.web.address=:80" - "--entryPoints.websecure.address=:443" + - "--entryPoints.ssh.address=:${GITEA_SHELL_SSH_PORT}" - "--providers.docker=true" - "--providers.docker.endpoint=unix:///var/run/docker.sock" - "--providers.docker.exposedByDefault=false" - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true" - # Email for Let's Encrypt (replace with yours) - - "--certificatesresolvers.letsencrypt.acme.email=callvaldemar@gmail.com" + - "--certificatesresolvers.letsencrypt.acme.email=${TRAEFIK_ACME_EMAIL}" - "--certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json" - "--metrics.prometheus=true" - "--metrics.prometheus.buckets=0.1,0.3,1.2,5.0" @@ -135,7 +185,10 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock - traefik-certificates:/etc/traefik/acme + networks: + - traefik-network ports: + - "${GITEA_SHELL_SSH_PORT}:${GITEA_SHELL_SSH_PORT}" - "80:80" - "443:443" healthcheck: @@ -146,8 +199,7 @@ services: start_period: 5s labels: - "traefik.enable=true" - # Traefik URL (replace with yours) - - "traefik.http.routers.dashboard.rule=Host(`traefik.gitea.heyvaldemar.net`)" + - "traefik.http.routers.dashboard.rule=Host(`${TRAEFIK_HOSTNAME}`)" - "traefik.http.routers.dashboard.service=api@internal" - "traefik.http.routers.dashboard.entrypoints=websecure" - "traefik.http.services.dashboard.loadbalancer.server.port=8080" @@ -155,10 +207,7 @@ services: - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt" - "traefik.http.services.dashboard.loadbalancer.passhostheader=true" - "traefik.http.routers.dashboard.middlewares=authtraefik" - # Basic Authentication for Traefik Dashboard - # Username: traefikadmin (replace with yours) - # Passwords must be encoded using MD5, SHA1, or BCrypt https://hostingcanada.org/htpasswd-generator/ - - "traefik.http.middlewares.authtraefik.basicauth.users=traefikadmin:$$2y$$10$$sMzJfirKC75x/hVpiINeZOiSm.Jkity9cn4KwNkRvO7hSQVFc5FLO" + - "traefik.http.middlewares.authtraefik.basicauth.users=${TRAEFIK_BASIC_AUTH}" - "traefik.http.routers.http-catchall.rule=HostRegexp(`{host:.+}`)" - "traefik.http.routers.http-catchall.entrypoints=web" - "traefik.http.routers.http-catchall.middlewares=redirect-to-https" @@ -166,43 +215,36 @@ services: restart: unless-stopped backups: - # Image tag (replace with yours) - image: postgres:14 - # Database backups prune interval (replace with yours). Default is 7 days. - # find /srv/gitea-postgres/backups -type f -mtime +7 | xargs rm -f - - # Application data backups prune interval (replace with yours). Default is 7 days. - # find /srv/gitea-application-data/backups -type f -mtime +7 | xargs rm -f - - # Gitea backups interval (replace with yours). Default is 1 day. - # sleep 24h - - # Run gitea-restore-application-data.sh to restore application data if needed. - # Run gitea-restore-database.sh to restore database if needed. - command: sh -c 'sleep 30m - && while true; do - PGPASSWORD="$$(echo $$POSTGRES_PASSWORD)" - pg_dump - -h postgres - -p 5432 - -d giteadb - -U giteadbuser | gzip > /srv/gitea-postgres/backups/gitea-postgres-backup-$$(date "+%Y-%m-%d_%H-%M").gz - && tar -zcpf /srv/gitea-application-data/backups/gitea-application-data-backup-$$(date "+%Y-%m-%d_%H-%M").tar.gz /etc/gitea - && find /srv/gitea-postgres/backups -type f -mtime +7 | xargs rm -f - && find /srv/gitea-application-data/backups -type f -mtime +7 | xargs rm -f; - sleep 24h; done' + image: ${GITEA_POSTGRES_IMAGE_TAG} + command: >- + sh -c 'sleep $BACKUP_INIT_SLEEP && + while true; do + pg_dump -h postgres -p 5432 -d $GITEA_DB_NAME -U $GITEA_DB_USER | gzip > $POSTGRES_BACKUPS_PATH/$POSTGRES_BACKUP_NAME-$(date "+%Y-%m-%d_%H-%M").gz && + tar -zcpf $DATA_BACKUPS_PATH/$DATA_BACKUP_NAME-$(date "+%Y-%m-%d_%H-%M").tar.gz $DATA_PATH && + find $POSTGRES_BACKUPS_PATH -type f -mtime +$POSTGRES_BACKUP_PRUNE_DAYS | xargs rm -f && + find $DATA_BACKUPS_PATH -type f -mtime +$DATA_BACKUP_PRUNE_DAYS | xargs rm -f; + sleep $BACKUP_INTERVAL; done' volumes: - - gitea-data:/etc/gitea - # Application data backups location - - gitea-data-backups:/srv/gitea-application-data/backups - # Database backups location - - gitea-postgres-backups:/srv/gitea-postgres/backups + - gitea-postgres-backup:/var/lib/postgresql/data + - gitea-data:${DATA_PATH} + - gitea-data-backups:${DATA_BACKUPS_PATH} + - gitea-database-backups:${POSTGRES_BACKUPS_PATH} environment: - # Database password (replace with yours) - POSTGRES_PASSWORD: etFneCEtAWRKkfeQmkvwLWE + GITEA_DB_NAME: ${GITEA_DB_NAME} + GITEA_DB_USER: ${GITEA_DB_USER} + PGPASSWORD: ${GITEA_DB_PASSWORD} + BACKUP_INIT_SLEEP: ${BACKUP_INIT_SLEEP} + BACKUP_INTERVAL: ${BACKUP_INTERVAL} + POSTGRES_BACKUP_PRUNE_DAYS: ${POSTGRES_BACKUP_PRUNE_DAYS} + DATA_BACKUP_PRUNE_DAYS: ${DATA_BACKUP_PRUNE_DAYS} + POSTGRES_BACKUPS_PATH: ${POSTGRES_BACKUPS_PATH} + DATA_BACKUPS_PATH: ${DATA_BACKUPS_PATH} + DATA_PATH: ${DATA_PATH} + POSTGRES_BACKUP_NAME: ${POSTGRES_BACKUP_NAME} + DATA_BACKUP_NAME: ${DATA_BACKUP_NAME} + networks: + - gitea-network restart: unless-stopped depends_on: postgres: condition: service_healthy - gitea: - condition: service_healthy