From 272b5e839efa7866ecf182e4c8aec1f34ca73bb0 Mon Sep 17 00:00:00 2001 From: Vladimir Mikhalev Date: Fri, 1 Nov 2024 17:15:50 -0400 Subject: [PATCH] Seafile with Let's Encrypt Using Docker Compose --- .env | 36 +++ .github/FUNDING.yml | 4 + .../workflows/00-deployment-verification.yml | 50 +++ .gitignore | 284 +++++++++++++++++ README.md | 133 ++++++++ seafile-restore-application-data.sh | 44 +++ seafile-restore-database.sh | 44 +++ ...ile-traefik-letsencrypt-docker-compose.yml | 289 ++++++++++++++++++ 8 files changed, 884 insertions(+) create mode 100644 .env create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/00-deployment-verification.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100755 seafile-restore-application-data.sh create mode 100755 seafile-restore-database.sh create mode 100644 seafile-traefik-letsencrypt-docker-compose.yml diff --git a/.env b/.env new file mode 100644 index 0000000..ae7c893 --- /dev/null +++ b/.env @@ -0,0 +1,36 @@ +# Traefik Variables +TRAEFIK_IMAGE_TAG=traefik:2.9 +TRAEFIK_LOG_LEVEL=WARN +TRAEFIK_ACME_EMAIL=callvaldemar@gmail.com +TRAEFIK_HOSTNAME=traefik.seafile.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 + +# Seafile Variables +SEAFILE_MARIADB_IMAGE_TAG=mariadb:11.1 +SEAFILE_IMAGE_TAG=h44z/seafile-ce:11.0.11 +SEAFILE_MEMCACHE_IMAGE_TAG=memcached:1.6.25-alpine +SEAFILE_DB_USER=seafiledbbuser +SEAFILE_DB_PASSWORD=DH8MowfpuwGyBfNcnafE +SEAFILE_DB_ADMIN_PASSWORD=MFfmVC4uJLyXVrjBwEDp +SEAFILE_SITE_NAME=Seafile +SEAFILE_ADMIN_USERNAME=seafileadmin@heyvaldemar.net +SEAFILE_ADMIN_PASSWORD=AdgTrAOJrqh78n713RsD +SEAFILE_HOSTNAME=seafile.heyvaldemar.net +# Timezone inside container +# A list of these tz database names can be looked up at Wikipedia +# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones +SEAFILE_TIMEZONE=America/New_York + +# Backup Variables +BACKUP_INIT_SLEEP=1m +BACKUP_INTERVAL=1m +MARIADB_BACKUP_PRUNE_DAYS=7 +DATA_BACKUP_PRUNE_DAYS=7 +MARIADB_BACKUPS_PATH=/srv/seafile-mariadb/backups +DATA_BACKUPS_PATH=/srv/seafile-application-data/backups +DATA_PATH=/seafile +MARIADB_BACKUP_NAME=seafile-mariadb-backup +DATA_BACKUP_NAME=seafile-application-data-backup \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..89b098c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +github: heyvaldemar +patreon: heyvaldemar +ko_fi: heyvaldemar +custom: ['paypal.com/paypalme/heyValdemarCOM', 'buymeacoffee.com/heyValdemar', 'ko-fi.com/heyValdemar'] diff --git a/.github/workflows/00-deployment-verification.yml b/.github/workflows/00-deployment-verification.yml new file mode 100644 index 0000000..6e004d6 --- /dev/null +++ b/.github/workflows/00-deployment-verification.yml @@ -0,0 +1,50 @@ +name: Deployment Verification + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + deploy-and-test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Create necessary Docker networks + run: | + docker network create seafile-network || true + docker network create traefik-network || true + + - name: Start up services using Docker Compose + run: docker compose -f seafile-traefik-letsencrypt-docker-compose.yml up -d + + - name: Modify /etc/hosts for internal routing + run: | + echo "127.0.0.1 seafile.heyvaldemar.net" | sudo tee -a /etc/hosts + echo "127.0.0.1 traefik.seafile.heyvaldemar.net" | sudo tee -a /etc/hosts + + - name: Print Docker Compose services status + run: docker ps + + - name: Wait for the application to be ready via Traefik + run: | + echo "Checking the routing and availability of application via Traefik..." + timeout 5m bash -c 'while ! curl -fsSLk "https://seafile.heyvaldemar.net"; do echo "Waiting for the application to be ready..."; sleep 10; done' + + - name: Inspect Network Configuration + run: | + docker network inspect seafile-network + docker network inspect traefik-network + + - name: Shutdown Docker Compose services + if: always() + run: docker compose -f seafile-traefik-letsencrypt-docker-compose.yml down 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/README.md b/README.md new file mode 100644 index 0000000..9d3b14c --- /dev/null +++ b/README.md @@ -0,0 +1,133 @@ +# Seafile with Let's Encrypt Using Docker Compose + +[![Deployment Verification](https://github.com/heyvaldemar/seafile-traefik-letsencrypt-docker-compose/actions/workflows/00-deployment-verification.yml/badge.svg)](https://github.com/heyvaldemar/seafile-traefik-letsencrypt-docker-compose/actions) + +The badge displayed on my repository indicates the status of the deployment verification workflow as executed on the latest commit to the main branch. + +**Passing**: This means the most recent commit has successfully passed all deployment checks, confirming that the Docker Compose setup functions correctly as designed. + +πŸ“™ The complete installation guide is available on my [website](https://www.heyvaldemar.com/install-seafile-using-docker-compose/). + +❗ Change variables in the `.env` to meet your requirements. + +πŸ’‘ Note that the `.env` file should be in the same directory as `seafile-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 seafile-network` + +Deploy Seafile using Docker Compose: + +`docker compose -f seafile-traefik-letsencrypt-docker-compose.yml -p seafile up -d` + +# Fixing CSRF Verification Error (403 Forbidden) in Seafile + +If you encounter a `403 Forbidden - CSRF verification failed` error when logging into Seafile, follow these steps to add your domain to the trusted origins and set the correct URL for the file server. + +Run the following command, replacing `https://seafile.heyvaldemar.net` with your Seafile domain: + +``` +export SEAFILE_DOMAIN="https://seafile.heyvaldemar.net" \ +&& SEAFILE_CONTAINER=$(docker ps -aqf "name=seafile-seafile") \ +&& docker exec -it $SEAFILE_CONTAINER /bin/sh -c "sed -i 's|SERVICE_URL = .*|SERVICE_URL = \"$SEAFILE_DOMAIN\"|' /opt/seafile/conf/seahub_settings.py" \ +&& docker exec -it $SEAFILE_CONTAINER /bin/sh -c "sed -i '/^CSRF_TRUSTED_ORIGINS/d' /opt/seafile/conf/seahub_settings.py && echo \"CSRF_TRUSTED_ORIGINS = ['$SEAFILE_DOMAIN']\" >> /opt/seafile/conf/seahub_settings.py" \ +&& docker exec -it $SEAFILE_CONTAINER /bin/sh -c "sed -i '/^FILE_SERVER_ROOT/d' /opt/seafile/conf/seahub_settings.py && echo \"FILE_SERVER_ROOT = '$SEAFILE_DOMAIN/seafhttp'\" >> /opt/seafile/conf/seahub_settings.py" \ +&& docker restart $SEAFILE_CONTAINER +``` + +This command will: + +1. Set `SERVICE_URL` to your domain (e.g., `https://seafile.heyvaldemar.net`). +2. Add the domain to `CSRF_TRUSTED_ORIGINS` to allow trusted requests. +3. Set `FILE_SERVER_ROOT` to your domain’s file server URL. +4. Restart the Seafile container to apply changes. + +After running the command, try logging in again. + +# Backups + +The `backups` container in the configuration is responsible for the following: + +1. **Database Backup**: Creates compressed backups of the MariaDB database using pg_dump. +Customizable backup path, filename pattern, and schedule through variables like `MARIADB_BACKUPS_PATH`, `MARIADB_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 `MARIADB_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. + +# seafile-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 `seafile-restore-database.shh` script executable, run the following command: + +`chmod +x seafile-restore-database.sh` + +Usage of this script ensures a controlled and guided process to restore the database from an existing backup. + +# seafile-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 `seafile-restore-application-data.sh` script executable, run the following command: + +`chmod +x seafile-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 + +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://mastodon.social/@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)\ +πŸͺ Support on [Ko-fi](https://ko-fi.com/heyValdemar)\ +πŸ’– Support on [PayPal](https://www.paypal.com/paypalme/heyValdemarCOM) diff --git a/seafile-restore-application-data.sh b/seafile-restore-application-data.sh new file mode 100755 index 0000000..05ef8cf --- /dev/null +++ b/seafile-restore-application-data.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# # seafile-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 `seafile-restore-application-data.sh` script executable, run the following command: +# `chmod +x seafile-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. + +SEAFILE_CONTAINER=$(docker ps -aqf "name=seafile-seafile") +SEAFILE_BACKUPS_CONTAINER=$(docker ps -aqf "name=seafile-backups") +BACKUP_PATH="/srv/seafile-application-data/backups/" +RESTORE_PATH="/seafile/" +BACKUP_PREFIX="seafile-application-data" + +echo "--> All available application data backups:" + +for entry in $(docker container exec -it "$SEAFILE_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: ${BACKUP_PREFIX}-backup-YYYY-MM-DD_hh-mm.tar.gz" +echo -n "--> " + +read SELECTED_APPLICATION_BACKUP + +echo "--> $SELECTED_APPLICATION_BACKUP was selected" + +echo "--> Stopping service..." +docker stop "$SEAFILE_CONTAINER" + +echo "--> Restoring application data..." +docker exec -it "$SEAFILE_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 "$SEAFILE_CONTAINER" \ No newline at end of file diff --git a/seafile-restore-database.sh b/seafile-restore-database.sh new file mode 100755 index 0000000..adfb0e2 --- /dev/null +++ b/seafile-restore-database.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# seafile-restore-all-databases.sh Description +# This script facilitates the restoration of all databases from a backup. + +SEAFILE_CONTAINER=$(docker ps -aqf "name=seafile-seafile") +SEAFILE_BACKUPS_CONTAINER=$(docker ps -aqf "name=seafile-backups") +SEAFILE_DB_USER="root" +MARIADB_PASSWORD=$(docker exec $SEAFILE_BACKUPS_CONTAINER printenv MARIADB_ROOT_PASSWORD) +BACKUP_PATH="/srv/seafile-mariadb/backups/" + +echo "--> All available database backups:" + +# Display all backups in the backup path +for entry in $(docker container exec "$SEAFILE_BACKUPS_CONTAINER" sh -c "ls $BACKUP_PATH") +do + echo "$entry" +done + +# Prompt user to select a backup +echo "--> Copy and paste the backup name from the list above to restore all databases and press [ENTER]" +echo "--> Example: seafile-mariadb-backup-YYYY-MM-DD_hh-mm.gz" +echo -n "--> " + +read SELECTED_DATABASE_BACKUP + +# Remove any surrounding quotes from the selected backup name +SELECTED_DATABASE_BACKUP=$(echo "$SELECTED_DATABASE_BACKUP" | tr -d "'\"") + +echo "--> $SELECTED_DATABASE_BACKUP was selected" + +# Stop the service container +echo "--> Stopping service..." +docker stop "$SEAFILE_CONTAINER" + +# Restore all databases +echo "--> Restoring all databases..." +docker exec "$SEAFILE_BACKUPS_CONTAINER" sh -c "mariadb -h mariadb -u $SEAFILE_DB_USER --password=$MARIADB_PASSWORD -e 'DROP DATABASE IF EXISTS seafiledb; DROP DATABASE IF EXISTS ccnet_db; DROP DATABASE IF EXISTS seahub_db;' \ +&& gunzip -c ${BACKUP_PATH}${SELECTED_DATABASE_BACKUP} | mariadb -h mariadb -u $SEAFILE_DB_USER --password=$MARIADB_PASSWORD" +echo "--> All databases have been restored." + +# Start the service container +echo "--> Starting service..." +docker start "$SEAFILE_CONTAINER" diff --git a/seafile-traefik-letsencrypt-docker-compose.yml b/seafile-traefik-letsencrypt-docker-compose.yml new file mode 100644 index 0000000..25b934a --- /dev/null +++ b/seafile-traefik-letsencrypt-docker-compose.yml @@ -0,0 +1,289 @@ +# Seafile with Let's Encrypt Using Docker Compose + +# The complete installation guide is available on my website https://www.heyvaldemar.com/install-seafile-using-docker-compose/ + +# Change variables in the `.env` to meet your requirements. +# Note that the `.env` file should be in the same directory as `seafile-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 seafile-network` + +# Deploy Seafile using Docker Compose: +# `docker compose -f seafile-traefik-letsencrypt-docker-compose.yml -p seafile up -d` + +# # Fixing CSRF Verification Error (403 Forbidden) in Seafile +# If you encounter a `403 Forbidden - CSRF verification failed` error when logging into Seafile, +# follow these steps to add your domain to the trusted origins and set the correct URL for the file server. + +# Run the following command, replacing `https://seafile.heyvaldemar.net` with your Seafile domain: +# ``` +# export SEAFILE_DOMAIN="https://seafile.heyvaldemar.net" \ +# && SEAFILE_CONTAINER=$(docker ps -aqf "name=seafile-seafile") \ +# && docker exec -it $SEAFILE_CONTAINER /bin/sh -c "sed -i 's|SERVICE_URL = .*|SERVICE_URL = \"$SEAFILE_DOMAIN\"|' /opt/seafile/conf/seahub_settings.py" \ +# && docker exec -it $SEAFILE_CONTAINER /bin/sh -c "sed -i '/^CSRF_TRUSTED_ORIGINS/d' /opt/seafile/conf/seahub_settings.py && echo \"CSRF_TRUSTED_ORIGINS = ['$SEAFILE_DOMAIN']\" >> /opt/seafile/conf/seahub_settings.py" \ +# && docker exec -it $SEAFILE_CONTAINER /bin/sh -c "sed -i '/^FILE_SERVER_ROOT/d' /opt/seafile/conf/seahub_settings.py && echo \"FILE_SERVER_ROOT = '$SEAFILE_DOMAIN/seafhttp'\" >> /opt/seafile/conf/seahub_settings.py" \ +# && docker restart $SEAFILE_CONTAINER +# ``` + +# This command will: +# 1. Set `SERVICE_URL` to your domain (e.g., `https://seafile.heyvaldemar.net`). +# 2. Add the domain to `CSRF_TRUSTED_ORIGINS` to allow trusted requests. +# 3. Set `FILE_SERVER_ROOT` to your domain’s file server URL. +# 4. Restart the Seafile container to apply changes. +# After running the command, try logging in again. + +# Backups +# The `backups` container in the configuration is responsible for the following: +# 1. **Database Backup**: Creates compressed backups of the MariaDB database using pg_dump. +# Customizable backup path, filename pattern, and schedule through variables like `MARIADB_BACKUPS_PATH`, `MARIADB_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 `MARIADB_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. + +# # seafile-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 `seafile-restore-database.shh` script executable, run the following command: +# `chmod +x seafile-restore-database.sh` +# Usage of this script ensures a controlled and guided process to restore the database from an existing backup. + +# # seafile-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 `seafile-restore-application-data.sh` script executable, run the following command: +# `chmod +x seafile-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 +# 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://mastodon.social/@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 +# Support on Ko-fi: https://ko-fi.com/heyValdemar +# Support on PayPal: https://www.paypal.com/paypalme/heyValdemarCOM + +networks: + seafile-network: + external: true + traefik-network: + external: true + +volumes: + mariadb-data: + seafile-data: + seafile-mariadb-backup: + seafile-data-backups: + seafile-database-backups: + traefik-certificates: + +services: + mariadb: + image: ${SEAFILE_MARIADB_IMAGE_TAG} + volumes: + - mariadb-data:/var/lib/mysql + environment: + MARIADB_USER: ${SEAFILE_DB_USER} + MARIADB_PASSWORD: ${SEAFILE_DB_PASSWORD} + MARIADB_ROOT_PASSWORD: ${SEAFILE_DB_ADMIN_PASSWORD} + networks: + - seafile-network + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 60s + restart: unless-stopped + + memcached: + command: memcached -m 256M + image: ${SEAFILE_MEMCACHE_IMAGE_TAG} + networks: + - seafile-network + restart: unless-stopped + + seafile: + image: ${SEAFILE_IMAGE_TAG} + volumes: + - seafile-data:${DATA_PATH} + environment: + MYSQL_SERVER: mariadb + MYSQL_USER: ${SEAFILE_DB_USER} + MYSQL_USER_PASSWORD: ${SEAFILE_DB_PASSWORD} + MYSQL_PORT: 3306 + SEAFILE_NAME: ${SEAFILE_SITE_NAME} + SEAFILE_ADDRESS: ${SEAFILE_HOSTNAME} + SEAFILE_ADMIN: ${SEAFILE_ADMIN_USERNAME} + SEAFILE_ADMIN_PW: ${SEAFILE_ADMIN_PASSWORD} + MYSQL_ROOT_PASSWORD: ${SEAFILE_DB_ADMIN_PASSWORD} + TIME_ZONE: ${SEAFILE_TIMEZONE} + networks: + - seafile-network + - traefik-network + healthcheck: + test: timeout 10s bash -c ':> /dev/tcp/127.0.0.1/8000' || exit 1 + interval: 10s + timeout: 5s + retries: 3 + start_period: 90s + labels: + - "traefik.enable=true" + # HTTP Router Seafile/Seahub + - "traefik.http.routers.seafile.rule=(Host(`${SEAFILE_HOSTNAME}`))" + - "traefik.http.routers.seafile.service=seafile" + - "traefik.http.routers.seafile.entrypoints=websecure" + - "traefik.http.services.seafile.loadbalancer.server.port=8000" + - "traefik.http.routers.seafile.tls=true" + - "traefik.http.routers.seafile.tls.certresolver=letsencrypt" + - "traefik.http.routers.seafile.middlewares=sec-headers" + # HTTP Router Seafdav + - "traefik.http.routers.seafile-dav.rule=Host(`${SEAFILE_HOSTNAME}`) && PathPrefix(`/seafdav`)" + - "traefik.http.routers.seafile-dav.service=seafile-dav" + - "traefik.http.routers.seafile-dav.entrypoints=websecure" + - "traefik.http.services.seafile-dav.loadbalancer.server.port=8080" + - "traefik.http.routers.seafile-dav.tls=true" + - "traefik.http.routers.seafile-dav.tls.certresolver=letsencrypt" + # HTTP Router Seafhttp + - "traefik.http.routers.seafile-http.rule=Host(`${SEAFILE_HOSTNAME}`) && PathPrefix(`/seafhttp`)" + - "traefik.http.routers.seafile-http.service=seafile-http" + - "traefik.http.routers.seafile-http.entrypoints=websecure" + - "traefik.http.services.seafile-http.loadbalancer.server.port=8082" + - "traefik.http.routers.seafile-http.tls=true" + - "traefik.http.routers.seafile-http.tls.certresolver=letsencrypt" + - "traefik.http.routers.seafile-http.middlewares=seafile-strip" + # Middlewares + - "traefik.http.middlewares.seafile-strip.stripprefix.prefixes=/seafhttp" + - "traefik.http.middlewares.sec-headers.headers.sslredirect=true" + - "traefik.http.middlewares.sec-headers.headers.browserXssFilter=true" + - "traefik.http.middlewares.sec-headers.headers.contentTypeNosniff=true" + - "traefik.http.middlewares.sec-headers.headers.forceSTSHeader=true" + - "traefik.http.middlewares.sec-headers.headers.stsIncludeSubdomains=true" + - "traefik.http.middlewares.sec-headers.headers.stsPreload=true" + - "traefik.http.middlewares.sec-headers.headers.referrerPolicy=same-origin" + - "traefik.docker.network=traefik-network" + restart: unless-stopped + depends_on: + mariadb: + condition: service_healthy + traefik: + condition: service_healthy + + traefik: + image: ${TRAEFIK_IMAGE_TAG} + command: + - "--log.level=${TRAEFIK_LOG_LEVEL}" + - "--accesslog=true" + - "--api.dashboard=true" + - "--api.insecure=true" + - "--ping=true" + - "--ping.entrypoint=ping" + - "--entryPoints.ping.address=:8082" + - "--entryPoints.web.address=:80" + - "--entryPoints.websecure.address=:443" + - "--providers.docker=true" + - "--providers.docker.endpoint=unix:///var/run/docker.sock" + - "--providers.docker.exposedByDefault=false" + - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true" + - "--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" + - "--global.checkNewVersion=true" + - "--global.sendAnonymousUsage=false" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - traefik-certificates:/etc/traefik/acme + networks: + - traefik-network + ports: + - "80:80" + - "443:443" + healthcheck: + test: ["CMD", "wget", "http://localhost:8082/ping","--spider"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 5s + labels: + - "traefik.enable=true" + - "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" + - "traefik.http.routers.dashboard.tls=true" + - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt" + - "traefik.http.services.dashboard.loadbalancer.passhostheader=true" + - "traefik.http.routers.dashboard.middlewares=authtraefik" + - "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" + - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" + restart: unless-stopped + + backups: + image: ${SEAFILE_MARIADB_IMAGE_TAG} + command: >- + sh -c 'sleep $BACKUP_INIT_SLEEP && + while true; do + mariadb-dump -h mariadb -u $SEAFILE_DB_USER -p"$SEAFILE_DB_PASSWORD" --all-databases | gzip > "$MARIADB_BACKUPS_PATH/$MARIADB_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 $MARIADB_BACKUPS_PATH -type f -mtime +$MARIADB_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: + - seafile-mariadb-backup:/var/lib/mysql + - seafile-data:${DATA_PATH} + - seafile-data-backups:${DATA_BACKUPS_PATH} + - seafile-database-backups:${MARIADB_BACKUPS_PATH} + environment: + SEAFILE_DB_USER: ${SEAFILE_DB_USER} + SEAFILE_DB_PASSWORD: ${SEAFILE_DB_PASSWORD} + MARIADB_ROOT_PASSWORD: ${SEAFILE_DB_ADMIN_PASSWORD} + BACKUP_INIT_SLEEP: ${BACKUP_INIT_SLEEP} + BACKUP_INTERVAL: ${BACKUP_INTERVAL} + MARIADB_BACKUP_PRUNE_DAYS: ${MARIADB_BACKUP_PRUNE_DAYS} + DATA_BACKUP_PRUNE_DAYS: ${DATA_BACKUP_PRUNE_DAYS} + MARIADB_BACKUPS_PATH: ${MARIADB_BACKUPS_PATH} + DATA_BACKUPS_PATH: ${DATA_BACKUPS_PATH} + DATA_PATH: ${DATA_PATH} + MARIADB_BACKUP_NAME: ${MARIADB_BACKUP_NAME} + DATA_BACKUP_NAME: ${DATA_BACKUP_NAME} + networks: + - seafile-network + restart: unless-stopped + depends_on: + mariadb: + condition: service_healthy