diff --git a/backup/Dockerfile b/backup/Dockerfile new file mode 100644 index 0000000..55340d8 --- /dev/null +++ b/backup/Dockerfile @@ -0,0 +1,7 @@ +FROM alpine:latest +RUN apk add --no-cache rsync tzdata + +COPY backup.sh /usr/local/bin/backup.sh +RUN chmod +x /usr/local/bin/backup.sh + +CMD ["crond", "-f", "-l", "2"] diff --git a/backup/backup.sh b/backup/backup.sh new file mode 100644 index 0000000..3c760b7 --- /dev/null +++ b/backup/backup.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# Format the current date +NOW=$(date +"%Y-%m-%d") + +# Create the archive directory if it does not exist +mkdir -p /backup/archive + +# Run rsync with an exclude file and a backup directory for changed/deleted files +rsync -av --delete \ + --exclude-from=/config/exclude.txt \ + --backup --backup-dir=/backup/archive/$NOW \ + /data/ /backup/latest/ + +# Find and delete archive directories older than 7 days +find /backup/archive/ -mindepth 1 -maxdepth 1 -type d -mtime +7 -exec rm -rf {} + diff --git a/backup/crontab.txt b/backup/crontab.txt new file mode 100644 index 0000000..5ff18ac --- /dev/null +++ b/backup/crontab.txt @@ -0,0 +1,2 @@ +# Run the backup every day at 2:00 AM +0 2 * * * /usr/local/bin/backup.sh diff --git a/backup/exclude.txt b/backup/exclude.txt new file mode 100644 index 0000000..cf2fe4e --- /dev/null +++ b/backup/exclude.txt @@ -0,0 +1,4 @@ +*.log +.backups +.DS_Store +._* diff --git a/docker-compose.yml b/docker-compose.yml index a2d6ebe..a6cec54 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,16 +5,12 @@ x-logging: &logging max-size: "5m" max-file: "2" -#x-firecrawl-service: &firecrawl-service -# image: ghcr.io/mendableai/firecrawl -# -#x-firecrawl-env: &firecrawl-env -# REDIS_URL: redis://redis:6379 -# REDIS_RATE_LIMIT_URL: redis://redis:6379 -# PLAYWRIGHT_MICROSERVICE_URL: http://playwright:3000/scrape - services: + ################################### + # FOUNDRY # + # https://rec.scarif.space/foundy # + ################################### foundry: profiles: ["prod"] <<: *logging @@ -44,6 +40,10 @@ services: networks: - nginx + ################################### + # NEXTCLOUD # + # https://tower.scarif.space # + ################################### nextcloud: profiles: ["prod", "dev"] <<: *logging @@ -119,6 +119,25 @@ services: - "tower.${DOMAIN}:${LOCAL_IP}" - "office.${DOMAIN}:${LOCAL_IP}" + ################################### + # VAULTWARDEN # + # https://vault.scarif.space # + ################################### + vaultwarden: + image: vaultwarden/server:latest + restart: unless-stopped + environment: + - "DOMAIN=https://vault.${DOMAIN}" + - VIRTUAL_HOST=vault.${DOMAIN} + - LETSENCRYPT_HOST=vault.${DOMAIN} + - VIRTUAL_PORT=80 + volumes: + - /mnt/tower/containers/vaultwarden:/data/ + + ################################### + # GITEA # + # https://labs.scarif.space # + ################################### gitea: profiles: ["prod", "dev"] <<: *logging @@ -155,6 +174,10 @@ services: depends_on: - db + ################################### + # AUDIOBOOKSHELF # + # https://echo.scarif.space # + ################################### audiobookshelf: image: ghcr.io/advplyr/audiobookshelf:latest volumes: @@ -170,6 +193,10 @@ services: networks: - nginx + ################################### + # GROCY # + # https://supplies.scarif.space # + ################################### grocy: image: lscr.io/linuxserver/grocy:latest environment: @@ -188,6 +215,10 @@ services: networks: - nginx + ################################### + # HOMEBOX # + # https://assets.scarif.space # + ################################### homebox: image: ghcr.io/sysadminsmedia/homebox:latest restart: always @@ -195,9 +226,7 @@ services: - HBOX_LOG_LEVEL=info - HBOX_LOG_FORMAT=text - HBOX_WEB_MAX_UPLOAD_SIZE=20 - # Please consider allowing analytics to help us improve Homebox (basic computer information, no personal data) - HBOX_OPTIONS_ALLOW_ANALYTICS=false - # Use a strong random string for the pepper in production, it will be used to hash API keys and make them more secure - HBOX_AUTH_API_KEY_PEPPER=${HOMEBOX_API_KEY_PEPPER} - HBOX_OPTIONS_ALLOW_REGISTRATION=true - VIRTUAL_HOST=assets.${DOMAIN} @@ -208,6 +237,10 @@ services: networks: - nginx + ################################### + # SEARXNG # + # https://holocron.scarif.space # + ################################### searxng: image: docker.io/searxng/searxng:latest restart: unless-stopped @@ -226,6 +259,9 @@ services: - CERT_NAME=${DOMAIN} <<: *logging + ################################### + # MINECRAFT # + ################################### minecraft: profiles: ["prod"] <<: *logging @@ -262,8 +298,12 @@ services: - PRUNE_BACKUPS_DAYS=30 volumes: - /mnt/tower/containers/minecraft:/data - - /mnt/backups/minecraft:/backups + - /mnt/tower/containers/minecraft/backup:/backups + ################################### + # NAVIDROME # + # https://radio.scarif.space # + ################################### navidrome: profiles: ["prod"] <<: *logging @@ -284,6 +324,10 @@ services: networks: - nginx + ################################### + # PIHOLE # + # https://net.scarif.space # + ################################### pihole: profiles: ["prod"] <<: *logging @@ -297,129 +341,194 @@ services: - TZ="Europe/London" - WEBPASSWORD=${PIHOLE_PASSWORD} volumes: - - "/docker/pihole/etc-pihole:/etc/pihole" - - "/docker/pihole/etc-dnsmasq.d:/etc/dnsmasq.d" + - "/mnt/tower/containers/pihole/etc-pihole:/etc/pihole" + - "./pihole/etc-dnsmasq.d:/etc/dnsmasq.d" restart: unless-stopped networks: - nginx - # jitsi: - # profiles: ["prod"] - # <<: *logging - # image: jitsi/web:stable - # restart: unless-stopped - # volumes: - # - ${CONFIG}/web:/config:Z - # - ${CONFIG}/web/crontabs:/var/spool/cron/crontabs:Z - # - ${CONFIG}/transcripts:/usr/share/jitsi-meet/transcripts:Z - # environment: - # - VIRTUAL_HOST=comms.${DOMAIN} - # - LETSENCRYPT_HOST=comms.${DOMAIN} - # - VIRTUAL_PORT=80 - # - CERT_NAME=${DOMAIN} - # - ENABLE_AUTH=1 - # - ENABLE_GUESTS=1 - # - ENABLE_PREJOIN_PAGE=1 - # - ENABLE_WELCOME_PAGE=1 - # - ENABLE_CLOSE_PAGE=1 - # - ENABLE_NOISY_MIC_DETECTION=1 - # - ETHERPAD_TITLE="Video Chat" - # - ETHERPAD_DEFAULT_PAD_TEXT="Welcome to Web Chat!\n\n" - # - ETHERPAD_SKIN_NAME="colibris" - # - ETHERPAD_SKIN_VARIANTS="super-light-toolbar super-light-editor light-background full-width-editor" - # - XMPP_BOSH_URL_BASE=https://xmpp.meet.jitsi:5280 - # - XMPP_AUTH_DOMAIN - # - XMPP_MUC_DOMAIN - # - XMPP_GUEST_DOMAIN - # - XMPP_RECORDER_DOMAIN - # networks: - # nginx: - # meet.jitsi: - # - # prosody: - # profiles: ["prod"] - # <<: *logging - # image: jitsi/prosody:stable - # restart: unless-stopped - # expose: - # - '${XMPP_PORT:-52222}' - # - '5347' - # - '5280' - # volumes: - # - ${CONFIG}/prosody/config:/config:Z - # - ${CONFIG}/prosody/prosody-plugins-custom:/prosody-plugins-custom:Z - # environment: - # - ENABLE_NOISY_MIC_DETECTION=1 - # - ENABLE_AUTH=1 - # - ENABLE_GUESTS=1 - # - ENABLE_LOBBY=1 - # - XMPP_DOMAIN - # - XMPP_AUTH_DOMAIN - # - XMPP_MUC_DOMAIN - # - XMPP_INTERNAL_MUC_DOMAIN - # - XMPP_GUEST_DOMAIN - # - JVB_AUTH_USER - # - JIGASI_XMPP_USER=jigasi - # - XMPP_RECORDER_DOMAIN - # - JIBRI_RECORDER_USER=recorder - # - JIBRI_XMPP_USER=jibri - # - JICOFO_AUTH_PASSWORD= - # networks: - # meet.jitsi: - # aliases: - # - ${XMPP_SERVER:-xmpp.meet.jitsi} - # - # # Focus component - # jicofo: - # profiles: ["prod"] - # <<: *logging - # image: jitsi/jicofo:stable - # restart: unless-stopped - # volumes: - # - ${CONFIG}/jicofo:/config:Z - # depends_on: - # - prosody - # environment: - # - ENABLE_AUTH=1 - # - XMPP_DOMAIN - # - XMPP_MUC_DOMAIN - # - XMPP_INTERNAL_MUC_DOMAIN - # - JVB_BREWERY_MUC - # - JIGASI_BREWERY_MUC=jigasibrewery - # - XMPP_RECORDER_DOMAIN - # - JIBRI_BREWERY_MUC=jibribrewery - # - JIBRI_PENDING_TIMEOUT=90 - # - JICOFO_AUTH_PASSWORD= - # networks: - # meet.jitsi: - # - # # Video bridge - # jvb: - # profiles: ["prod"] - # <<: *logging - # image: jitsi/jvb:stable - # restart: unless-stopped - # ports: - # - '${JVB_PORT:-10000}:${JVB_PORT:-10000}/udp' - # - '127.0.0.1:${JVB_COLIBRI_PORT:-8080}:8080' - # volumes: - # - ${CONFIG}/jvb:/config:Z - # depends_on: - # - prosody - # environment: - # - DOCKER_HOST_ADDRESS=${LOCAL_IP} - # - XMPP_SERVER - # - XMPP_AUTH_DOMAIN - # - XMPP_INTERNAL_MUC_DOMAIN - # - JVB_BREWERY_MUC - # - JVB_AUTH_USER - # - JVB_STUN_SERVERS=meet-jit-si-turnrelay.jitsi.net:443 - # - JVB_PORT - # networks: - # meet.jitsi: - # aliases: - # - jvb.meet.jitsi + ################################### + # DAWARICH # + # https://map.scarif.space # + ################################### + dawarich_app: + image: freikin/dawarich:latest + container_name: dawarich_app + volumes: + - /mnt/tower/containers/dawarich/public:/var/app/public + - /mnt/tower/containers/dawarich/watched:/var/app/tmp/imports/watched + - /mnt/tower/containers/dawarich/storage:/var/app/storage + - /mnt/tower/containers/dawarich/data:/dawarich_db_data + networks: + - dawarich + - redis + - nginx + stdin_open: true + tty: true + entrypoint: web-entrypoint.sh + command: ['bin/rails', 'server', '-p', '3000', '-b', '::'] + restart: unless-stopped + environment: + VIRTUAL_HOST: maps.${DOMAIN} + VIRTUAL_PORT: 3000 + LETSENCRYPT_HOST: maps.${DOMAIN} + RAILS_ENV: ${RAILS_ENV:-production} + PUID: ${PUID:-1000} + PGID: ${PGID:-1000} + REDIS_URL: ${REDIS_URL:-redis://redis:6379} + DATABASE_HOST: ${DATABASE_HOST:-dawarich_db} + DATABASE_PORT: ${DATABASE_PORT:-5432} + DATABASE_USERNAME: ${DATABASE_USERNAME:-postgres} + DATABASE_PASSWORD: ${DATABASE_PASSWORD:-password} + DATABASE_NAME: ${DATABASE_NAME:-dawarich_development} + APPLICATION_HOSTS: maps.${DOMAIN},localhost,::1,127.0.0.1 + TIME_ZONE: ${TIME_ZONE:-Europe/London} + APPLICATION_PROTOCOL: ${APPLICATION_PROTOCOL:-http} + PROMETHEUS_EXPORTER_ENABLED: ${PROMETHEUS_EXPORTER_ENABLED:-false} + SECRET_KEY_BASE: ${DAWARICH_SECRET_KEY} + RAILS_LOG_TO_STDOUT: ${RAILS_LOG_TO_STDOUT:-true} + SELF_HOSTED: ${SELF_HOSTED:-true} + STORE_GEODATA: ${STORE_GEODATA:-true} + WEB_CONCURRENCY: ${WEB_CONCURRENCY:-1} + <<: *logging + depends_on: + - dawarich_db + - redis + deploy: + resources: + limits: + cpus: ${APP_CPU_LIMIT:-0.50} + memory: ${APP_MEMORY_LIMIT:-4G} + dawarich_sidekiq: + image: freikin/dawarich:latest + container_name: dawarich_sidekiq + volumes: + - /mnt/tower/containers/dawarich/public:/var/app/public + - /mnt/tower/containers/dawarich/watched:/var/app/tmp/imports/watched + - /mnt/tower/containers/dawarich/storage:/var/app/storage + networks: + - dawarich + - redis + stdin_open: true + tty: true + entrypoint: sidekiq-entrypoint.sh + command: ['sidekiq'] + restart: unless-stopped + environment: + RAILS_ENV: ${RAILS_ENV:-production} + ### To run under a non-root user, set PUID/PGID here (do NOT use `user:`). + # PUID: ${PUID:-1000} + # PGID: ${PGID:-1000} + REDIS_URL: ${REDIS_URL:-redis://redis:6379} + DATABASE_HOST: ${DATABASE_HOST:-dawarich_db} + DATABASE_PORT: ${DATABASE_PORT:-5432} + DATABASE_USERNAME: ${DATABASE_USERNAME:-postgres} + DATABASE_PASSWORD: ${DATABASE_PASSWORD:-password} + DATABASE_NAME: ${DATABASE_NAME:-dawarich_development} + APPLICATION_HOSTS: maps.${DOMAIN},localhost,::1,127.0.0.1 + BACKGROUND_PROCESSING_CONCURRENCY: ${BACKGROUND_PROCESSING_CONCURRENCY:-3} + APPLICATION_PROTOCOL: ${APPLICATION_PROTOCOL:-http} + PROMETHEUS_EXPORTER_ENABLED: ${PROMETHEUS_EXPORTER_ENABLED:-false} + SECRET_KEY_BASE: ${DAWARICH_SECRET_KEY} + RAILS_LOG_TO_STDOUT: ${RAILS_LOG_TO_STDOUT:-true} + SELF_HOSTED: ${SELF_HOSTED:-true} + STORE_GEODATA: ${STORE_GEODATA:-true} + <<: *logging + depends_on: + - dawarich_db + - redis + - dawarich_app + + dawarich_db: + image: postgis/postgis:17-3.5-alpine + shm_size: 1G + container_name: dawarich_db + volumes: + - /mnt/tower/containers/dawarich/db:/var/lib/postgresql/data + networks: + - dawarich + <<: *logging + environment: + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} + POSTGRES_DB: ${POSTGRES_DB:-dawarich_development} + restart: always + + ################################### + # SURE # + # https://credits.scarif.space # + ################################### + + sure_app: + image: ghcr.io/we-promise/sure:stable + volumes: + - /mnt/tower/containers/sure:/rails/storage + restart: unless-stopped + <<: *logging + environment: + VIRTUAL_HOST: credits.${DOMAIN} + VIRTUAL_PORT: 3000 + LETSENCRYPT_HOST: credits.${DOMAIN} + POSTGRES_USER: ${POSTGRES_USER:-sure_user} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-sure_password} + POSTGRES_DB: ${POSTGRES_DB:-sure_production} + SECRET_KEY_BASE: ${SURE_SECRET_KEY} + SELF_HOSTED: "true" + RAILS_FORCE_SSL: "false" + RAILS_ASSUME_SSL: "false" + DB_HOST: sure_db + DB_PORT: 5432 + REDIS_URL: redis://redis:6379/1 + depends_on: + - sure_db + - redis + networks: + - sure + - nginx + - redis + + sure_worker: + image: ghcr.io/we-promise/sure:stable + command: bundle exec sidekiq + <<: *logging + volumes: + - /mnt/tower/containers/sure:/rails/storage + restart: unless-stopped + depends_on: + - sure_db + - redis + environment: + POSTGRES_USER: ${POSTGRES_USER:-sure_user} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-sure_password} + POSTGRES_DB: ${POSTGRES_DB:-sure_production} + SECRET_KEY_BASE: ${SURE_SECRET_KEY} + SELF_HOSTED: "true" + RAILS_FORCE_SSL: "false" + RAILS_ASSUME_SSL: "false" + DB_HOST: sure_db + DB_PORT: 5432 + REDIS_URL: redis://redis:6379/1 + networks: + - sure + - redis + + sure_db: + image: postgres:16 + <<: *logging + restart: unless-stopped + volumes: + - /mnt/tower/containers/sure/db:/var/lib/postgresql/data + environment: + POSTGRES_USER: ${POSTGRES_USER:-sure_user} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-sure_password} + POSTGRES_DB: ${POSTGRES_DB:-sure_production} + networks: + - sure + + ################################### + # UTILITIES # + ################################### db: profiles: ["prod", "dev"] <<: *logging @@ -445,6 +554,7 @@ services: - redis mongodb: + <<: *logging profiles: ["prod", "dev"] image: mongo restart: always @@ -456,7 +566,16 @@ services: environment: - MONGO_INITDB_ROOT_USERNAME=${DB_USER} - MONGO_INITDB_ROOT_PASSWORD=${DB_PASSWORD} + + rsync-backup: <<: *logging + build: ./backup + volumes: + - /mnt/tower:/data:ro + - /mnt/backup/tower:/backup + - ./backup/crontab.txt:/etc/crontabs/root:ro + - ./backup/exclude.txt:/config/exclude.txt:ro + restart: unless-stopped nginx: <<: *logging @@ -525,5 +644,7 @@ networks: db: nginx: redis: + sure: + dawarich: meet.jitsi: diff --git a/pihole/etc-dnsmasq.d/02-wildcard.conf b/pihole/etc-dnsmasq.d/02-wildcard.conf new file mode 100644 index 0000000..2d71233 --- /dev/null +++ b/pihole/etc-dnsmasq.d/02-wildcard.conf @@ -0,0 +1 @@ +address=/scarif.space/192.168.2.158