From abd2c152874f72fdfcc7de8cce4dd50eda0ea8b9 Mon Sep 17 00:00:00 2001 From: DoTheEvolution Date: Fri, 10 Apr 2020 00:52:11 +0200 Subject: [PATCH] update --- README.md | 13 ++ bitwarden_rs/readme.md | 180 ++++++++++++++++++++++ bookstack/readme.md | 246 +++++++++++++++++++++++++++++++ borg_backup/readme.md | 99 +++++++++++++ ddclient/readme.md | 61 ++++++++ homer/assets/tools/bitwarden.png | Bin 0 -> 8403 bytes homer/assets/tools/bookstack.png | Bin 0 -> 5782 bytes homer/assets/tools/grafana.png | Bin 0 -> 13832 bytes homer/assets/tools/nextcloud.png | Bin 0 -> 2637 bytes homer/assets/tools/portainer.png | Bin 0 -> 2139 bytes homer/readme.md | 83 +++++++++++ nextcloud/readme.md | 226 ++++++++++++++++++++++++++++ portainer/readme.md | 80 ++++++++++ watchtower/readme.md | 55 +++++++ 14 files changed, 1043 insertions(+) create mode 100644 bitwarden_rs/readme.md create mode 100644 bookstack/readme.md create mode 100644 borg_backup/readme.md create mode 100644 ddclient/readme.md create mode 100644 homer/assets/tools/bitwarden.png create mode 100644 homer/assets/tools/bookstack.png create mode 100644 homer/assets/tools/grafana.png create mode 100644 homer/assets/tools/nextcloud.png create mode 100644 homer/assets/tools/portainer.png create mode 100644 homer/readme.md create mode 100644 nextcloud/readme.md create mode 100644 portainer/readme.md create mode 100644 watchtower/readme.md diff --git a/README.md b/README.md index b1cb357..69ff4f2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,15 @@ # selfhosted-apps-docker Guide by Example + +--------- + +* bitwarden_rs - password manager +* bookstack - notes and documentation +* borg_backup - backup utility +* homer - home page +* nextcloud - file share & sync +* portaine - docker managment + +how to get them runnig in each folder + +containers dont have ports cuz caddy reverse proxy diff --git a/bitwarden_rs/readme.md b/bitwarden_rs/readme.md new file mode 100644 index 0000000..a97ed07 --- /dev/null +++ b/bitwarden_rs/readme.md @@ -0,0 +1,180 @@ +# Bitwarden_rs in docker + +###### guide by example + +![logo](https://i.imgur.com/BQ9Ec6f.png) + +### Purpose + +Password manager. RS version is simpler and lighter than the official bitwarden. + +* [Official site](https://bitwarden.com/) +* [Github](https://github.com/dani-garcia/bitwarden_rs) +* [DockerHub image used](https://hub.docker.com/r/bitwardenrs/server) + +### Files and directory structure + + ``` + /home + └── ~ + └── docker + └── bitwarden + ├── 🗁 bitwarden-backup + ├── 🗁 bitwarden-data + ├── 🗋 .env + ├── 🗋 docker-compose.yml + └── 🗋 bitwarden-backup-script.sh + ``` + +### docker-compose + + [Documentation](https://github.com/dani-garcia/bitwarden_rs/wiki/Using-Docker-Compose) on compose. + + `docker-compose.yml` + + ``` + version: "3" + services: + + bitwarden: + image: bitwardenrs/server + hostname: bitwarden + container_name: bitwarden + restart: unless-stopped + volumes: + - ./bitwarden-data/:/data/ + environment: + - TZ + - ADMIN_TOKEN + - DOMAIN + - SIGNUPS_ALLOWED + - SMTP_SSL + - SMTP_EXPLICIT_TLS + - SMTP_HOST + - SMTP_PORT + - SMTP_USERNAME + - SMTP_PASSWORD + - SMTP_FROM + + networks: + default: + external: + name: $DEFAULT_NETWORK + ``` + + `.env` + + ``` + # GENERAL + MY_DOMAIN=blabla.org + DEFAULT_NETWORK=caddy_net + TZ=Europe/Prague + + # BITWARDEN + ADMIN_TOKEN=YdLo1TM4MYEQ948GOVZ29IF4fABSrZMpk9 + DOMAIN=https://passwd.blabla.org + SIGNUPS_ALLOWED=true + + # USING SENDGRID FOR SENDING EMAILS + SMTP_SSL=true + SMTP_EXPLICIT_TLS=true + SMTP_HOST=smtp.sendgrid.net + SMTP_PORT=465 + SMTP_USERNAME=apikey + SMTP_PASSWORD=SG.MOQQegA3bgfodRN4IG2Wqwe.s23Ld4odqhOQQegf4466A4 + SMTP_FROM=admin@blabla.org + ``` + +### Reverse proxy + + Caddy v2 is used, details [here.](https://github.com/DoTheEvo/Caddy-v2-examples)
+ Bitwarden_rs documentation has a [section on reverse proxy.](https://github.com/dani-garcia/bitwarden_rs/wiki/Proxy-examples) + + `Caddyfile` + ``` + { + # acme_ca https://acme-staging-v02.api.letsencrypt.org/directory + } + + passwd.{$MY_DOMAIN} { + encode gzip + reverse_proxy /notifications/hub/negotiate bitwarden:80 + reverse_proxy /notifications/hub bitwarden:3012 + reverse_proxy bitwarden:80 + } + ``` + +### Forward port 3012 on your router + + - websocket protocol used for some kind of notifications + +### Extra info + + * **bitwarden can be managed** at `passwd.blabla.org/admin` and entering `ADMIN_TOKEN` set in the `.env` file + +![interface-pic](https://i.imgur.com/5LxEUsA.png) + +### Update + + * [watchtower](https://github.com/DoTheEvo/docker-selfhosted-projects/tree/master/watchtower) updates the image automaticly + + * manual image update
+ `docker-compose pull`
+ `docker-compose up -d`
+ `docker image prune` + +### Backup and restore + + * **backup** using [borgbackup setup](https://github.com/DoTheEvo/docker-selfhosted-projects/tree/master/borg_backup) + that makes daily backup of the entire directory + + * **restore**
+ down the bitwarden container `docker-compose down`
+ delete the entire bitwarden directory
+ from the backup copy back the bitwarden directortory
+ start the container `docker-compose up -d` + +### Backup of just user data + +For additional peace of mind. +Having user-data daily exported using the [official procedure.](https://github.com/dani-garcia/bitwarden_rs/wiki/Backing-up-your-vault)
+For bitwarden_rs it means sqlite database dump and the content of the `attachments` folder. +The backup files are overwriten on every run of the script, +but borg backup is backing the entire directory in to snapshots daily, so no need for some keeping-last-X consideration. + +* **install sqlite on the host system** + +* **create backup script**
+ placed inside `bitwarden` directory on the host + + `make_bitwarden_backup.sh` + ``` + #!/bin/sh + + # GO IN TO THE DIRECTORY WHERE THIS SCRIPT RESIDES + cd "${0%/*}" + + # CREATE BACKUP DIRECTORY IF IT DOES NOT EXIST + mkdir -p ./bitwarden-backup + + # CREATE SQLITE BACKUP + sqlite3 ./bitwarden-data/db.sqlite3 ".backup './bitwarden-BACKUP.db.sqlite3'" + + # BACKUP ATTACHMENTS + tar -czvf ./bitwarden-backup/attachments.tar.gz ./bitwarden-data/attachments + ``` + + the script must be executabe - `chmod +x make_bitwarden_backup.sh` + +* **cronjob** on the host
+ `crontab -e` - add new cron job
+ `0 2 * * * /home/bastard/docker/bitwarden/bitwarden-backup-script.sh` - run it [at 02:00](https://crontab.guru/#0_2_*_*_*)
+ `crontab -l` - list cronjobs + +### Restore the user data + + - down the container `docker-compose down`
+ - replace `db.sqlite3` with the one from the backup + - replace attachments folder with the one from the backup + - start the container `docker-compose up -d` + diff --git a/bookstack/readme.md b/bookstack/readme.md new file mode 100644 index 0000000..039a154 --- /dev/null +++ b/bookstack/readme.md @@ -0,0 +1,246 @@ +# Bookstack in docker + +###### guide by example + +![logo](https://i.imgur.com/qDXwqaU.png) + +### Purpose + +Documentation and notes. + +* [Official site](https://www.bookstackapp.com/) +* [Github](https://github.com/BookStackApp/BookStack) +* [DockerHub image used](https://hub.docker.com/r/linuxserver/bookstack) + +### Files and directory structure + + ``` + /home + └── ~ + └── docker + └── bookstack + ├── 🗁 bookstack-data + ├── 🗁 bookstack-data-db + ├── 🗁 bookstack-backup + ├── 🗋 .env + ├── 🗋 docker-compose.yml + └── 🗋 bookstack-backup-script.sh + ``` + +### docker-compose + + Dockerhub linuxserver/bookstack [example compose.](https://hub.docker.com/r/linuxserver/bookstack) + + `docker-compose.yml` + + ``` + version: "2" + services: + + bookstack: + image: linuxserver/bookstack + container_name: bookstack + hostname: bookstack + environment: + - PUID + - PGID + - DB_HOST + - DB_USER + - DB_PASS + - DB_DATABASE + - APP_URL + volumes: + - ./bookstack-data:/config + restart: unless-stopped + depends_on: + - bookstack_db + + bookstack_db: + image: linuxserver/mariadb + container_name: bookstack_db + hostname: bookstack_db + environment: + - PUID + - PGID + - MYSQL_ROOT_PASSWORD + - TZ + - MYSQL_DATABASE + - MYSQL_USER + - MYSQL_PASSWORD + volumes: + - ./bookstack-data-db:/config + restart: unless-stopped + + networks: + default: + external: + name: $DEFAULT_NETWORK + ``` + + `.env` + + ``` + # GENERAL + MY_DOMAIN=blabla.org + DEFAULT_NETWORK=caddy_net + TZ=Europe/Prague + + # BOOKSTACK + PUID=1000 + PGID=1000 + DB_HOST=bookstack_db + DB_USER=bookstack + DB_PASS=bookstack + DB_DATABASE=bookstackapp + APP_URL=https://book.blabla.org + + # BOOKSTACK-MARIADB + PUID=1000 + PGID=1000 + MYSQL_ROOT_PASSWORD=bookstack + MYSQL_DATABASE=bookstackapp + MYSQL_USER=bookstack + MYSQL_PASSWORD=bookstack + ``` + +### reverse proxy + + caddy v2 is used, + details [here](https://github.com/DoTheEvo/Caddy-v2-examples) + + `Caddyfile` + ``` + { + # acme_ca https://acme-staging-v02.api.letsencrypt.org/directory + } + + book.{$MY_DOMAIN} { + reverse_proxy { + to bookstack:80 + } + } + ``` + +### update + + * [watchguard]() updates the image automaticly + + * manual image update
+ `docker-compose pull`
+ `docker-compose up -d`
+ `docker image prune` + +### backup and restore + + * **backup** using [borgbackup setup](https://github.com/DoTheEvo/docker-selfhosted-projects/tree/master/borg_backup) + that makes daily backup of the entire directory + + * **restore**
+ down the bookstack containers `docker-compose down`
+ delete the entire bookstack directory
+ from the backup copy back the bookstack directortory
+ start the container `docker-compose up -d` + +### Backup of just user data + +For additional peace of mind. +Having user-data daily exported using the [official procedure.](https://www.bookstackapp.com/docs/admin/backup-restore/)
+For bookstack it means database dump and the content of several directories +containing user uploaded files. +The backup files are overwriten on every run of the script, +but borg backup is backing entire directory in to snapshots, so no need for some keeping-last-X consideration. + + * **database backup**
+ script `make_backup.sh` placed in to `bookstack_db` container, + in to `/config` directory that is bind mounted to the host machine.
+ made executable `chmod +x make_backup.sh` inside the container + + - This script creates path `/config/backups-db`
+ - deletes all files in the backup path except 30 newest
+ - creates new mysqldump using env variables passed from `.env` file
+ + `make_backup.sh` + ``` + #!/bin/bash + + # ----------------------------------------------- + NUMB_BACKUPS_TO_KEEP=30 + BACKUP_PATH=/config/backups-db + BACKUP_NAME=$(date +"%s").bookstack.database.backup.sql + # ----------------------------------------------- + + mkdir -p $BACKUP_PATH + + cd $BACKUP_PATH + ls -tr | head -n -$NUMB_BACKUPS_TO_KEEP | xargs --no-run-if-empty rm + + mysqldump -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE > $BACKUP_PATH/$BACKUP_NAME + ``` + + * **files backup**
+ script `make_backup.sh` placed in to `bookstack` container, + in to `/config` directory that is bind mounted to the host machine.
+ made executable `chmod +x make_backup.sh` inside the container + + - This script creates path `/config/backups-files`
+ - deletes all files in the backup path except 30 newest
+ - creates new archive containing uploaded files
+ + `make_backup.sh` + ``` + #!/bin/bash + + # ----------------------------------------------- + NUMB_BACKUPS_TO_KEEP=30 + BACKUP_PATH=/config/backups-files + BACKUP_NAME=$(date +"%s").bookstack.files.backup.tar.gz + # ----------------------------------------------- + + mkdir -p $BACKUP_PATH + + cd $BACKUP_PATH + ls -tr | head -n -$NUMB_BACKUPS_TO_KEEP | xargs --no-run-if-empty rm + + cd /config/www + tar -czvf $BACKUP_PATH/$BACKUP_NAME .env uploads files images + ``` + + * **automatic periodic execution of the backup scripts** + + Using cron running on the host machine that will execute scripts inside containers. + + script `cron_job_do_backups.sh` inside `~/docker/bookstack` + + `cron_job_do_backups.sh` + ``` + #!/bin/bash + + docker exec bookstack /config/make_backup.sh + docker exec bookstack_db /config/make_backup.sh + ``` + + `chmod +x cron_job_do_backups.sh` on the host machine + + `crontab -e` + + `0 2 * * * /home/bastard/docker/bookstack/cron_job_do_backups.sh` + +### restore official way + + * restore of the database + + copy the backup sql dump file in to the bind mount `bookstack-data-db` directory + + exec in to the container and tell mariadb to restore data from the copied file + + `docker exec -it bookstack_db /bin/bash`
+ `cd /config`
+ `mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE < 1584566634.bookstack.database.backup.sql` + + * restore of the files + + copy the backup gz.tar archive in to bind mount `bookstack-data/www/` directory
+ + `docker exec -it bookstack /bin/bash`
+ `cd /config/www`
+ `tar -xvzf 1584566633.bookstack.files.backup.tar.gz` diff --git a/borg_backup/readme.md b/borg_backup/readme.md new file mode 100644 index 0000000..e943115 --- /dev/null +++ b/borg_backup/readme.md @@ -0,0 +1,99 @@ +# BorgBackup in docker + +###### guide by example + +### purpose + +Backup terminal utility. + +* [Official site](https://www.borgbackup.org/) +* [Github](https://github.com/borgbackup/borg) + +### files and directory structure + + ``` + /home + └── ~ + ├── borg_backup + │ ├── 🗁 docker_backup + │ ├── 🗋 borg-backup.sh + │ └── 🗋 borg_backup.log + │ + └── docker + ├── container #1 + ├── container #2 + └── ... + ``` + +### borg-backup.sh + + `borg-backup.sh` + + ``` + #!/bin/bash + + # INITIALIZE THE REPO WITH THE COMMAND: + # borg init --encryption=none /mnt/C1/backup_borg/ + # THEN RUN THIS SCRIPT + + # ----------------------------------------------- + + BACKUP_THIS='/home/spravca/docker /etc' + REPOSITORY='/home/spravca/borg_backup/docker_backup' + LOGFILE='/home/spravca/borg_backup/borg_backup.log' + + # ----------------------------------------------- + + NOW=$(date +"%Y-%m-%d | %H:%M | ") + echo "$NOW Starting Backup and Prune" >> $LOGFILE + + # CREATES NEW ARCHIVE IN PRESET REPOSITORY + + borg create \ + $REPOSITORY::'{now:%s}' \ + $BACKUP_THIS \ + \ + --compression zstd \ + --one-file-system \ + --exclude-caches \ + --exclude-if-present '.nobackup' \ + --exclude '/home/*/Downloads/' \ + + # DELETES ARCHIVES NOT FITTING KEEP-RULES + + borg prune -v --list $REPOSITORY \ + --keep-daily=7 \ + --keep-weekly=4 \ + --keep-monthly=6 \ + --keep-yearly=0 \ + + echo "$NOW Done" >> $LOGFILE + borg list $REPOSITORY >> $LOGFILE + echo '------------------------------' >> $LOGFILE + + # --- USEFULL SHIT --- + + # setup above ignores directories containing '.nobackup' file + # make '.nobackup' imutable using chattr to prevent accidental removal + # touch .nobackup + # chattr +i .nobackup + + # in the repo folder, to list available backups: + # borg list . + # to mount one of them: + # borg mount .::1584472836 ~/temp + # to umount: + # borg umount ~/temp + # to delete single backup in a repo: + # borg delete .::1584472836 + ``` + +### automatic execution + +* make the script executable `chmod +x borg-backup.sh` + +* cron job, every day at 3:00 + + `crontab -e` + + `0 3 * * * /home/bastard/borg_backup/borg-backup.sh` diff --git a/ddclient/readme.md b/ddclient/readme.md new file mode 100644 index 0000000..b842f1f --- /dev/null +++ b/ddclient/readme.md @@ -0,0 +1,61 @@ +# ddclient + +###### guide by example + +### purpose + +Automatic DNS entries update. Useful if no static IP from ISP. + +* [Github](https://github.com/ddclient/ddclient) + +### files and directory structure + + ``` + /etc + └── ddclient + └── 🗋 ddclient.conf + ``` + +### configuration + +Example is for cloudflare managed DNS. + + `ddclient.conf` + + ``` + daemon=300 + syslog=yes + mail=root + mail-failure=root + pid=/var/run/ddclient.pid + ssl=yes + + use=web, web=checkip.dyndns.org/, web-skip='IP Address' + wildcard=yes + + ## + ## CloudFlare (www.cloudflare.com) + ## + protocol=cloudflare, \ + zone=blabla.org, \ + ttl=1, \ + login=bastard.blabla@gmail.com, \ + password=global-api-key-goes-here \ + blabla.org,*.blabla.org + + ## + protocol=cloudflare, \ + zone=blabla.tech, \ + ttl=1, \ + login=bastard.blabla@gmail.com, \ + password=global-api-key-goes-here \ + blabla.tech,*.blabla.tech + ``` + +### reverse proxy + + no web interface + +### update + + during host linux package update diff --git a/homer/assets/tools/bitwarden.png b/homer/assets/tools/bitwarden.png new file mode 100644 index 0000000000000000000000000000000000000000..be45706eba5eeb47178cd3ce0d8cffd026df8b1b GIT binary patch literal 8403 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
  • 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 KMNUMnLSTZXqF~nm literal 0 HcmV?d00001 diff --git a/homer/assets/tools/nextcloud.png b/homer/assets/tools/nextcloud.png new file mode 100644 index 0000000000000000000000000000000000000000..47d24492914c2b830ab02207e02cc75151237db9 GIT binary patch literal 2637 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 R$Z-Gw002ovPDHLkV1jibI$Qt% literal 0 HcmV?d00001 diff --git a/homer/readme.md b/homer/readme.md new file mode 100644 index 0000000..56605c2 --- /dev/null +++ b/homer/readme.md @@ -0,0 +1,83 @@ +# Homer in docker + +###### guide by example + +### purpose + +Homepage. + +* [Github](https://github.com/bastienwirtz/homer) +* [DockerHub image used](https://hub.docker.com/r/linuxserver/bookstack) + +### files and directory structure + + ``` + /home + └── ~ + └── docker + └── homer + ├── 🗁 assets + ├── 🗋 .config.yml + ├── 🗋 .env + └── 🗋 docker-compose.yml + ``` + +### docker-compose + + `docker-compose.yml` + + ``` + version: "2" + services: + homer: + image: b4bz/homer:latest + container_name: homer + hostname: homer + volumes: + - .config.yml:/www/config.yml + - ./assets/:/www/assets + restart: unless-stopped + expose: + - "8080" + + networks: + default: + external: + name: $DEFAULT_NETWORK + ``` + + `.env` + + ``` + # GENERAL + MY_DOMAIN=blabla.org + DEFAULT_NETWORK=caddy_net + ``` + +### reverse proxy + + caddy v2 is used, + details [here](https://github.com/DoTheEvo/Caddy-v2-examples) + + `Caddyfile` + ``` + { + # acme_ca https://acme-staging-v02.api.letsencrypt.org/directory + } + + netdata.{$MY_DOMAIN} { + reverse_proxy { + to netdata:80 + } + } + ``` + +### update + + * image update using docker compose + + `docker-compose pull`
    + `docker-compose up -d`
    + `docker image prune` + + diff --git a/nextcloud/readme.md b/nextcloud/readme.md new file mode 100644 index 0000000..9796789 --- /dev/null +++ b/nextcloud/readme.md @@ -0,0 +1,226 @@ +# Nextcloud in docker + +###### guide by example + +chapters + +1. [Docker compose](#1-docker-compose) +2. [Reverse proxy using caddy v2](#2-Reverse-proxy-using-caddy-v2) +3. [Some stuff afterwards](#3-Some-stuff-afterwards) +4. [Update Nextcloud](#4-Update-Nextcloud) +5. [Backup and restore](#5-Backup-and-restore) + +# #1 Docker compose + +Official examples [here](https://github.com/nextcloud/docker/tree/master/.examples/docker-compose) + +There are several options, default recomendation is apache. +Alternative is fpm php as stand alone container with either apache or ngnix.
    +The default apache with php as a module is used in this setup + +- **Create a new docker network**
    `docker network create caddy_net`
    +All nextcloud containers must be on the same network. + +- **Create a directory structure** +Where nextcloud docker stuff will be organized.
    +Here will be `~/docker/nextcloud`.
    + + ``` + /home + └── ~ + └── docker + └── nextcloud + ├── nextcloud-data + ├── .env + └── docker-compose.yml + ``` + + - `nextcloud-data` the directory where '/var/www/html' will be bind-mounted + - `.env` the env file with the variables + - `docker-compose.yml` the compose file + + +- **Create `.env` file**
    + + `.env` + ``` + # GENERAL + MY_DOMAIN=blabla.org + DEFAULT_NETWORK=caddy_net + + # NEXTCLOUD-MARIADB + MYSQL_ROOT_PASSWORD=nextcloud + MYSQL_PASSWORD=nextcloud + MYSQL_DATABASE=nextcloud + MYSQL_USER=nextcloud + ``` + +- **Create `docker-compose.yml` file**
    + Four containers are spin up + - nextcloud-db - mariadb database where files and users meta data are stored + - nextcloud-redis - in memory data store for more responsive interface + - nextcloud-app - the nextcloud + - nextcloud-cron - for being able to run maintnance cronjobs + + Two persinstent storages + - nextcloud-db named volume - nextcloud-db:/var/lib/mysql + - nextcloud-app bind mount - ./nextcloud-data/:/var/www/html + + `docker-compose.yml` + + ``` + version: '3' + + services: + + nextcloud-db: + image: mariadb + container_name: nextcloud-db + hostname: nextcloud-db + command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW + restart: unless-stopped + volumes: + - nextcloud-db:/var/lib/mysql + environment: + - MYSQL_ROOT_PASSWORD + - MYSQL_PASSWORD + - MYSQL_DATABASE + - MYSQL_USER + + nextcloud-redis: + image: redis:alpine + container_name: nextcloud-redis + hostname: nextcloud-redis + restart: unless-stopped + + nextcloud-app: + image: nextcloud:apache + container_name: nextcloud + hostname: nextcloud + restart: unless-stopped + depends_on: + - nextcloud-db + - nextcloud-redis + links: + - nextcloud-db + ports: + - 8080:80 + volumes: + - ./nextcloud-data/:/var/www/html + environment: + - MYSQL_HOST=nextcloud-db + - REDIS_HOST=nextcloud-redis + - NEXTCLOUD_TRUSTED_DOMAINS + + nextcloud-cron: + image: nextcloud:apache + container_name: nextcloud-cron + hostname: nextcloud-cron + restart: unless-stopped + volumes: + - ./nextcloud-data/:/var/www/html + entrypoint: /cron.sh + depends_on: + - nextcloud-db + - nextcloud-redis + + volumes: + nextcloud-db: + + networks: + default: + external: + name: $DEFAULT_NETWORK + + ``` + +- **Run docker compose** + + `docker-compose -f docker-compose.yml up -d` + +# #2 Reverse proxy using caddy v2 + + Provides reverse proxy so that more services can run on this docker host,
    + and also provides https.
    + This is a basic setup, for more details here is + [Caddy v2 tutorial + examples](https://github.com/DoTheEvo/Caddy-v2-examples) + +- **Have nextcloud to Caddyfile**
    + + `Caddyfile` + ``` + { + # acme_ca https://acme-staging-v02.api.letsencrypt.org/directory + } + + nextcloud.{$MY_DOMAIN} { + reverse_proxy { + to nextcloud:80 + } + } + + ``` + +- **Create docker-compose.yml**
    + + `docker-compose.yml` + ``` + version: "3.7" + + services: + caddy: + image: "caddy/caddy:alpine" + container_name: "caddy" + hostname: "caddy" + ports: + - "80:80" + - "443:443" + volumes: + - "./Caddyfile:/etc/caddy/Caddyfile:ro" + - caddy_lets_encrypt_storage:/data + - caddy_config_storage:/config + environment: + - MY_DOMAIN + + networks: + default: + external: + name: $DEFAULT_NETWORK + + volumes: + caddy_lets_encrypt_storage: + caddy_config_storage: + ``` + Make sure docker-compose.yml has the .env file with the same variables for + $DEFAULT_NETWORK and $MY_DOMAIN + +- **Run it** + + `docker-compose -f docker-compose.yml up -d` + + If something is fucky use `docker logs caddy` to see what is happening. + Restarting the container can help getting the certificates, if its stuck there. + Or investigate inside `docker container exec -it caddy /bin/sh`, + trying to ping hosts that are suppose to be reachable for example. + +# #3. Some stuff afterwards + + - in settings > overview, nextcloud complains about missing indexes or big int + - docker exec -it nextcloud /bin/sh + - chsh -s /bin/sh www-data + - su www-data + - cd /var/www/html + - php occ db:add-missing-indices + - php occ db:convert-filecache-bigint + +# #4 Update Nextcloud + + `docker-compose pull` + `docker-compose up -d` + `docker image prune` + + +# #5.Backup and restore + + likely there will be container running borg or borgmatic and cron + diff --git a/portainer/readme.md b/portainer/readme.md new file mode 100644 index 0000000..c9b6cff --- /dev/null +++ b/portainer/readme.md @@ -0,0 +1,80 @@ +# Portainer in docker + +###### guide by example + +### purpose + +User friendly overview of running containers. + +### files and directory structure + + ``` + /home + └── ~ + └── docker + └── portainer + ├── 🗁 portainer_data + ├── 🗋 .env + └── 🗋 docker-compose.yml + ``` + +### docker-compose + + `docker-compose.yml` + + ``` + version: '2' + + services: + portainer: + image: portainer/portainer + container_name: portainer + hostname: portainer + command: -H unix:///var/run/docker.sock + restart: unless-stopped + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./portainer_data:/data + environment: + - TZ + + networks: + default: + external: + name: $DEFAULT_NETWORK + ``` + + `.env` + + ``` + # GENERAL + MY_DOMAIN=blabla.org + DEFAULT_NETWORK=caddy_net + TZ=Europe/Prague + ``` + +### reverse proxy + + caddy v2 is used, + details [here](https://github.com/DoTheEvo/Caddy-v2-examples) + + `Caddyfile` + ``` + { + # acme_ca https://acme-staging-v02.api.letsencrypt.org/directory + } + + portainer.{$MY_DOMAIN} { + reverse_proxy { + to portainer:9000 + } + } + ``` + +### update + + * image update using docker compose + + `docker-compose pull`
    + `docker-compose up -d`
    + `docker image prune` diff --git a/watchtower/readme.md b/watchtower/readme.md new file mode 100644 index 0000000..ccc127e --- /dev/null +++ b/watchtower/readme.md @@ -0,0 +1,55 @@ +# Watchtower in docker + +###### guide by example + +### purpose + +Automatic updates of containers. + +* [Github](https://github.com/containrrr/watchtower) +* [DockerHub image used](https://hub.docker.com/r/containrrr/watchtower) + +### files and directory structure + + ``` + /home + └── ~ + └── docker + └── watchtower + └── 🗋 docker-compose.yml + ``` + +### docker-compose + + [scheduled](https://pkg.go.dev/github.com/robfig/cron@v1.2.0?tab=doc#hdr-CRON_Expression_Format) + to run every saturday at midnight
    + no need to be on the same network as other containers, no need .env file
    + + `docker-compose.yml` + + ``` + version: '3' + services: + watchtower: + image: containrrr/watchtower:latest + container_name: watchtower + hostname: watchtower + restart: unless-stopped + environment: + - TZ=Europe/Prague + - WATCHTOWER_SCHEDULE=0 0 0 * * SAT + - WATCHTOWER_CLEANUP=true + - WATCHTOWER_TIMEOUT=30s + - WATCHTOWER_DEBUG=false + - WATCHTOWER_INCLUDE_STOPPED=false + volumes: + - /var/run/docker.sock:/var/run/docker.sock + ``` + +### reverse proxy + + no web interface + +### update + + it updates itself