From 47aa1a348451c4a36b219cda0fae10722cc67dc4 Mon Sep 17 00:00:00 2001 From: Moni Ghaoui Date: Mon, 14 Apr 2025 19:51:40 +0200 Subject: [PATCH] FC --- .gitignore | 1 + containers/Bastille/Bastille.md | 213 +++++++++++++ containers/Bastille/Caddy.md | 42 +++ containers/Bastille/MariaDB.md | 31 ++ containers/Bastille/Nextcloud/Nextcloud.md | 73 +++++ containers/Bastille/Nextcloud/nginx.conf | 265 +++++++++++++++++ containers/Bastille/Postgres.md | 54 ++++ containers/Bastille/nginx.md | 10 + containers/docker/audiobookshelf/compose.yaml | 13 + containers/docker/httpd/compose.yaml | 9 + containers/docker/httpd/www/index.html | 8 + containers/docker/immich/compose.yaml | 84 ++++++ .../docker/immich/hwaccel.transcoding.yml | 54 ++++ containers/docker/jellyfin/compose.yaml | 30 ++ containers/docker/multitenant/.gitignore | 3 + containers/docker/multitenant/README.md | 55 ++++ .../multitenant/infra/docker-compose.yml | 31 ++ .../multitenant/jellyfin/run_with_podman.sh | 12 + .../multitenant/lemmy/customPostgresql.conf | 30 ++ .../multitenant/lemmy/docker-compose.yml | 114 +++++++ .../docker/multitenant/lemmy/lemmy.hjson | 19 ++ .../multitenant/lemmy/nginx_internal.conf | 96 ++++++ .../multitenant/mastodon/.env.production | 28 ++ .../multitenant/mastodon/docker-compose.yml | 137 +++++++++ .../docker/multitenant/moni0_proxy_host.png | Bin 0 -> 46430 bytes .../docker/multitenant/moni1_proxy_host.png | Bin 0 -> 46089 bytes .../docker/multitenant/nextcloud/REAME.md | 2 + .../multitenant/nextcloud/docker-compose.yml | 17 ++ .../sites/moni8080/docker-compose.yml | 12 + .../multitenant/sites/moni8080/index.html | 8 + .../sites/moni8081/docker-compose.yml | 12 + .../multitenant/sites/moni8081/index.html | 8 + .../multitenant/sites/moni8082/index.html | 8 + .../docker/multitenant/wordpress/Dockerfile | 7 + .../multitenant/wordpress/docker-compose.yml | 43 +++ containers/docker/nextcloud/README.md | 40 +++ containers/docker/nextcloud/compose.yaml | 67 +++++ containers/docker/nextcloud/restart.sh | 13 + containers/docker/nextcloud/stop.sh | 13 + containers/docker/photoprism/README.md | 3 + .../docker/photoprism/docker-compose.yml | 152 ++++++++++ containers/docker/pinepods/compose.yaml | 31 ++ .../docker/qbittorrent/docker-compose.yml | 42 +++ containers/docker/webtop/compose.yaml | 24 ++ containers/docker/windows/compose.yaml | 18 ++ .../docker/wordpress/docker-compose.yml | 31 ++ homeservers/Backup.md | 86 ++++++ homeservers/Immich.md | 15 + homeservers/Jellyfin.md | 21 ++ homeservers/Nextcloud.md | 22 ++ homeservers/PhotoPrism.md | 43 +++ homeservers/README.md | 43 +++ homeservers/RPI.md | 53 ++++ homeservers/Restore.md | 62 ++++ homeservers/qBittorrent.md | 11 + homeservers/scripts/cloud_backup.service | 8 + homeservers/scripts/cloud_backup.sh | 3 + homeservers/scripts/cloud_backup.timer | 9 + homeservers/scripts/nextcloud_backup.sh | 1 + homeservers/scripts/photoprism_backup.sh | 59 ++++ homeservers/scripts/photoprism_import.service | 8 + homeservers/scripts/photoprism_import.timer | 9 + laptops/Clevo_Laptop.md | 179 +++++++++++ laptops/Ideapad_510.md | 88 ++++++ laptops/Laptops.md | 2 + os/AlmaLinux/AlmaLinux.md | 90 ++++++ os/FreeBSD/FreeBSD_on_Clevo_Laptop.md | 280 ++++++++++++++++++ os/FreeBSD/FreeBSD_on_Lenovo_Thinkcentre.md | 111 +++++++ os/FreeBSD/FreeBSD_on_RPI.md | 94 ++++++ os/FreeBSD/Storage.md | 53 ++++ ssh.md | 4 + 71 files changed, 3317 insertions(+) create mode 100644 .gitignore create mode 100644 containers/Bastille/Bastille.md create mode 100644 containers/Bastille/Caddy.md create mode 100644 containers/Bastille/MariaDB.md create mode 100644 containers/Bastille/Nextcloud/Nextcloud.md create mode 100644 containers/Bastille/Nextcloud/nginx.conf create mode 100644 containers/Bastille/Postgres.md create mode 100644 containers/Bastille/nginx.md create mode 100644 containers/docker/audiobookshelf/compose.yaml create mode 100644 containers/docker/httpd/compose.yaml create mode 100644 containers/docker/httpd/www/index.html create mode 100644 containers/docker/immich/compose.yaml create mode 100644 containers/docker/immich/hwaccel.transcoding.yml create mode 100644 containers/docker/jellyfin/compose.yaml create mode 100644 containers/docker/multitenant/.gitignore create mode 100644 containers/docker/multitenant/README.md create mode 100644 containers/docker/multitenant/infra/docker-compose.yml create mode 100755 containers/docker/multitenant/jellyfin/run_with_podman.sh create mode 100644 containers/docker/multitenant/lemmy/customPostgresql.conf create mode 100644 containers/docker/multitenant/lemmy/docker-compose.yml create mode 100644 containers/docker/multitenant/lemmy/lemmy.hjson create mode 100644 containers/docker/multitenant/lemmy/nginx_internal.conf create mode 100644 containers/docker/multitenant/mastodon/.env.production create mode 100644 containers/docker/multitenant/mastodon/docker-compose.yml create mode 100644 containers/docker/multitenant/moni0_proxy_host.png create mode 100644 containers/docker/multitenant/moni1_proxy_host.png create mode 100644 containers/docker/multitenant/nextcloud/REAME.md create mode 100644 containers/docker/multitenant/nextcloud/docker-compose.yml create mode 100644 containers/docker/multitenant/sites/moni8080/docker-compose.yml create mode 100644 containers/docker/multitenant/sites/moni8080/index.html create mode 100644 containers/docker/multitenant/sites/moni8081/docker-compose.yml create mode 100644 containers/docker/multitenant/sites/moni8081/index.html create mode 100644 containers/docker/multitenant/sites/moni8082/index.html create mode 100644 containers/docker/multitenant/wordpress/Dockerfile create mode 100644 containers/docker/multitenant/wordpress/docker-compose.yml create mode 100644 containers/docker/nextcloud/README.md create mode 100644 containers/docker/nextcloud/compose.yaml create mode 100755 containers/docker/nextcloud/restart.sh create mode 100755 containers/docker/nextcloud/stop.sh create mode 100644 containers/docker/photoprism/README.md create mode 100644 containers/docker/photoprism/docker-compose.yml create mode 100644 containers/docker/pinepods/compose.yaml create mode 100644 containers/docker/qbittorrent/docker-compose.yml create mode 100644 containers/docker/webtop/compose.yaml create mode 100644 containers/docker/windows/compose.yaml create mode 100644 containers/docker/wordpress/docker-compose.yml create mode 100644 homeservers/Backup.md create mode 100644 homeservers/Immich.md create mode 100644 homeservers/Jellyfin.md create mode 100644 homeservers/Nextcloud.md create mode 100644 homeservers/PhotoPrism.md create mode 100644 homeservers/README.md create mode 100644 homeservers/RPI.md create mode 100644 homeservers/Restore.md create mode 100644 homeservers/qBittorrent.md create mode 100644 homeservers/scripts/cloud_backup.service create mode 100644 homeservers/scripts/cloud_backup.sh create mode 100644 homeservers/scripts/cloud_backup.timer create mode 100644 homeservers/scripts/nextcloud_backup.sh create mode 100644 homeservers/scripts/photoprism_backup.sh create mode 100644 homeservers/scripts/photoprism_import.service create mode 100644 homeservers/scripts/photoprism_import.timer create mode 100644 laptops/Clevo_Laptop.md create mode 100644 laptops/Ideapad_510.md create mode 100644 laptops/Laptops.md create mode 100644 os/AlmaLinux/AlmaLinux.md create mode 100644 os/FreeBSD/FreeBSD_on_Clevo_Laptop.md create mode 100644 os/FreeBSD/FreeBSD_on_Lenovo_Thinkcentre.md create mode 100644 os/FreeBSD/FreeBSD_on_RPI.md create mode 100644 os/FreeBSD/Storage.md create mode 100644 ssh.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/containers/Bastille/Bastille.md b/containers/Bastille/Bastille.md new file mode 100644 index 0000000..9d70d9a --- /dev/null +++ b/containers/Bastille/Bastille.md @@ -0,0 +1,213 @@ +# Bastille + +This is my guide for getting Bastille BSD up-and-running. + +First make sure that FreeBSD is up-to-date: + +*NOTE* This takes a *long* time on a Raspberry PI. Only do this if you have a lot of time on your hands! + +You may be smart to `tmux` first. + +```sh +tmux +``` + +```sh +freebsd-update fetch install +``` + +```sh +reboot +``` + +After reboot, check again: + +```sh +freebsd-update install +``` + +Verify your version: + +```sh +freebsd-version +``` + +## Setup + +First we need to make a backup of `pf.conf`, if you already setup pf before, otherwise you can skip this step. + +```sh +mv /etc/pf.conf /etc/pf.conf.backup +``` + +And then + +```sh +bastille setup +``` + +This will setup the loopback interface and create a `/etc/pf.conf`. + +You need to manually add the following to `/etc/pf.conf`, at the bottom, in order to allow http, https and RDP: + +``` +pass in inet proto tcp from any to any port { 80, 443, 3389 } flags S/SA keep state +``` + +Then start it: + +```sh +service pf start +``` + +The `bastille setup` will try to configure the wrong config file and complain. We need to fix the zfs stuff manually. + +And change, assuming you created a zpool named `data`. + +For example (WATCH OUT, BELOW COMMAND IS DANGEROUS): + +```sh +zpool create -f data /dev/ada0 +``` + +Change bastille.conf + +```sh +nvim /usr/local/etc/bastille/bastille.conf +``` + +``` +bastille_zfs_enable="YES" +bastille_zfs_zpool="data" +``` + +And just in case, run the setup again: + +```sh +bastille setup zfs +``` + +## Start + +Ok, now start Bastille: + +```sh +service bastille restart +``` + +Bootstrap: + +```sh +bastille bootstrap 14.2-RELEASE update +``` + +## Create a container + +Figure out your network card: + +```sh +ifconfig +``` + +You don't want the loopback but your real card that connects to the internet. The KVM virtual machine has `vtnet0` and the Raspberry PI has `genet0`, the Lenovo Thinkcentre has `em0`. + +```sh +# Lenovo Thinkcentre +bastille create alcatraz 14.2-RELEASE 192.168.1.201 em0 +``` + +If you want to have exlusive packages in the jail and not share the host packages, do this: + +```sh +bastille pkg alcatraz bootstrap +bastille pkg alcatraz update +``` + +Alternatively, you can mount the package cache: + +```sh +# Optional +bastille mount alcatraz /var/cache/pkg/ /var/cache/pkg/ nullfs rw 0 0 +``` + +I like to install my favorites since I use them quite often: + +```sh +bastille pkg alcatraz install -y tmux git neovim +``` + +Test it: + +```sh +bastille pkg alcatraz install -y apache24 +bastille sysrc alcatraz apache24_enable=YES +bastille service alcatraz apache24 start +``` + +Now go to the ip address with your browser on another machine: + +http://192.168.1.201/ + +You should see "It works!" + +Alternatively: + +```sh +curl http://192.168.1.201/ +``` + +You should see: + +```html +

It works!

+``` + +Now destroy it: + +```sh +bastille stop alcatraz +bastille destroy force alcatraz +``` + +# Using ports + +```sh +bastille create alcatraz 14.2-RELEASE 192.168.1.201 em0 +bastille pkg alcatraz bootstrap +bastille pkg alcatraz update +bastille pkg alcatraz install -y git +bastille cmd alcatraz git clone --depth 1 https://git.FreeBSD.org/ports.git /usr/ports +``` + +and then go in the console: + +```sh +bastille console alcatraz +``` + +within the console... + +```sh +export BATCH=yes +cd /usr/ports/www/apache24/ && make install clean +exit +``` + +enable and start it ... + +```sh +bastille sysrc alcatraz apache24_enable=YES +bastille service alcatraz apache24 start +``` + +Test it: + +```sh +curl http://192.168.1.201/ +``` + +Destroy it: + +```sh +bastille destroy force alcatraz +``` diff --git a/containers/Bastille/Caddy.md b/containers/Bastille/Caddy.md new file mode 100644 index 0000000..fca7ad8 --- /dev/null +++ b/containers/Bastille/Caddy.md @@ -0,0 +1,42 @@ +# Caddy + +```sh +bastille create caddy 14.2-RELEASE 192.168.1.200 genet0 +bastille pkg caddy bootstrap +bastille pkg caddy update +bastille pkg caddy install -y caddy +``` + +After installing Caddy you see some instructions. To reshow them: + +```sh +bastille pkg caddy info -D caddy +``` + +Edit the Caddyfile: + +```sh +bastille pkg caddy install -y tmux neovim +bastille console caddy +tmux +nvim /usr/local/etc/caddy/Caddyfile +``` + +Start the service. + +```sh +bastille service caddy caddy enable +bastille service caddy caddy start +``` + +See the logs: + +```sh +bastille cmd caddy cat /var/log/caddy/caddy.log +``` + +To quickly look at the caddyfile: + +```sh +bastille cmd caddy cat /usr/local/etc/caddy/Caddyfile +``` \ No newline at end of file diff --git a/containers/Bastille/MariaDB.md b/containers/Bastille/MariaDB.md new file mode 100644 index 0000000..4166b3d --- /dev/null +++ b/containers/Bastille/MariaDB.md @@ -0,0 +1,31 @@ +# MariaDB + +```sh +bastille pkg alcatraz install -y mariadb114-server mariadb114-client +``` + +Repeat the message: + +```sh +bastille pkg alcatraz info -D mariadb114-server +``` + +Enable and start + +```sh +bastille sysrc alcatraz mysql_enable=YES +bastille service alcatraz mysql-server start +``` + +Create nextcloud database and user + +```sh +bastille cmd alcatraz mysql +``` + +```sql +CREATE DATABASE nextcloud; +CREATE USER 'nextcloud'@'192.168.1.201' IDENTIFIED BY '1234sys!'; +GRANT ALL PRIVILEGES ON nextcloud.* TO 'nextcloud'@'192.168.1.201'; +FLUSH PRIVILEGES; +``` diff --git a/containers/Bastille/Nextcloud/Nextcloud.md b/containers/Bastille/Nextcloud/Nextcloud.md new file mode 100644 index 0000000..81586b4 --- /dev/null +++ b/containers/Bastille/Nextcloud/Nextcloud.md @@ -0,0 +1,73 @@ +# Nextcloud + +My standard setup: + +```sh +bastille create alcatraz 14.2-RELEASE 192.168.1.201 em0 +bastille mount alcatraz /var/cache/pkg/ /var/cache/pkg/ nullfs rw 0 0 +bastille pkg alcatraz install -y tmux git neovim sudo +``` + +## Trying it my way + +```sh +bastille pkg alcatraz install -y php83 nextcloud-php83 php83-pecl-APCu php83-extensions sd nginx +``` + +Repeat the message: + +```sh +bastille pkg alcatraz info -D www/nextcloud +``` + +Additional: + +```sh +bastille cmd alcatraz cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini +bastille cmd alcatraz sd 'memory_limit = 128M' 'memory_limit = -1' /usr/local/etc/php.ini +``` + +We need to go in and out of the console for this one: + +```sh +bastille console alcatraz +echo 'apc.enable_cli = 1' >> /usr/local/etc/php.ini +exit +``` + +Follow the instructions for [MariaDB](../MariaDB.md). + +Now run the installer + +```sh +bastille cmd alcatraz sudo -u www php /usr/local/www/nextcloud/occ maintenance:install \ +--database='mysql' --database-host='127.0.0.1' --database-name='nextcloud' \ +--database-user='nextcloud' --database-pass='1234sys!' \ +--admin-user='admin' --admin-pass='1234sys!' +``` + +You should see: + +``` +[alcatraz]: +Nextcloud was successfully installed +[alcatraz]: 0 +``` + +Edit nginx.conf for nextcloud. + +```sh +bastille cmd alcatraz nvim /usr/local/etc/nginx/nginx.conf +``` + +Use [this file](./nginx.conf). + +It's based on: https://docs.nextcloud.com/server/stable/admin_manual/installation/nginx.html#nextcloud-in-the-webroot-of-nginx + + +```sh +bastille service alcatraz php_fpm enable +bastille service alcatraz php_fpm start +bastille service alcatraz nginx enable +bastille service alcatraz nginx start +``` \ No newline at end of file diff --git a/containers/Bastille/Nextcloud/nginx.conf b/containers/Bastille/Nextcloud/nginx.conf new file mode 100644 index 0000000..45971b1 --- /dev/null +++ b/containers/Bastille/Nextcloud/nginx.conf @@ -0,0 +1,265 @@ +#user nobody; +worker_processes 1; + +# This default error log path is compiled-in to make sure configuration parsing +# errors are logged somewhere, especially during unattended boot when stderr +# isn't normally logged anywhere. This path will be touched on every nginx +# start regardless of error log location configured here. See +# https://trac.nginx.org/nginx/ticket/147 for more info. +# +#error_log /var/log/nginx/error.log; +# + +#pid logs/nginx.pid; + +events { + worker_connections 1024; +} + + +http { + include mime.types; + default_type application/octet-stream; + + #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + # '$status $body_bytes_sent "$http_referer" ' + # '"$http_user_agent" "$http_x_forwarded_for"'; + + #access_log logs/access.log main; + + sendfile on; + #tcp_nopush on; + + #keepalive_timeout 0; + keepalive_timeout 65; + + #gzip on; + + upstream php-handler { + server 127.0.0.1:9000; + #server unix:/var/run/php/php7.4-fpm.sock; + } + + # Set the `immutable` cache control options only for assets with a cache busting `v` argument + map $arg_v $asset_immutable { + "" ""; + default ", immutable"; + } + + # server { + # listen 80; + # listen [::]:80; + # server_name cloud.example.com; + # # enforce https + # return 301 https://$server_name:443$request_uri; + # } + + server { + #listen 80; + #listen [::]:80; + server_name 192.168.1.201; + + # Path to the root of your installation + root /usr/local/www/nextcloud; + + # Use Mozilla's guidelines for SSL/TLS settings + # https://mozilla.github.io/server-side-tls/ssl-config-generator/ + #ssl_certificate /etc/ssl/nginx/cloud.example.com.crt; + #ssl_certificate_key /etc/ssl/nginx/cloud.example.com.key; + + # Prevent nginx HTTP Server Detection + server_tokens off; + + # HSTS settings + # WARNING: Only add the preload option once you read about + # the consequences in https://hstspreload.org/. This option + # will add the domain to a hardcoded list that is shipped + # in all major browsers and getting removed from this list + # could take several months. + #add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" always; + + # set max upload size and increase upload timeout: + client_max_body_size 512M; + client_body_timeout 300s; + fastcgi_buffers 64 4K; + + # Enable gzip but do not remove ETag headers + gzip on; + gzip_vary on; + gzip_comp_level 4; + gzip_min_length 256; + gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; + gzip_types application/atom+xml text/javascript application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; + + # Pagespeed is not supported by Nextcloud, so if your server is built + # with the `ngx_pagespeed` module, uncomment this line to disable it. + #pagespeed off; + + # The settings allows you to optimize the HTTP2 bandwidth. + # See https://blog.cloudflare.com/delivering-http-2-upload-speed-improvements/ + # for tuning hints + client_body_buffer_size 512k; + + # HTTP response headers borrowed from Nextcloud `.htaccess` + add_header Referrer-Policy "no-referrer" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Permitted-Cross-Domain-Policies "none" always; + add_header X-Robots-Tag "noindex, nofollow" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Remove X-Powered-By, which is an information leak + fastcgi_hide_header X-Powered-By; + + # Set .mjs and .wasm MIME types + # Either include it in the default mime.types list + # and include that list explicitly or add the file extension + # only for Nextcloud like below: + include mime.types; + types { + text/javascript mjs; + application/wasm wasm; + } + + # Specify how to handle directories -- specifying `/index.php$request_uri` + # here as the fallback means that Nginx always exhibits the desired behaviour + # when a client requests a path that corresponds to a directory that exists + # on the server. In particular, if that directory contains an index.php file, + # that file is correctly served; if it doesn't, then the request is passed to + # the front-end controller. This consistent behaviour means that we don't need + # to specify custom rules for certain paths (e.g. images and other assets, + # `/updater`, `/ocs-provider`), and thus + # `try_files $uri $uri/ /index.php$request_uri` + # always provides the desired behaviour. + index index.php index.html /index.php$request_uri; + + # Rule borrowed from `.htaccess` to handle Microsoft DAV clients + location = / { + if ( $http_user_agent ~ ^DavClnt ) { + return 302 /remote.php/webdav/$is_args$args; + } + } + + location = /robots.txt { + allow all; + log_not_found off; + access_log off; + } + + # Make a regex exception for `/.well-known` so that clients can still + # access it despite the existence of the regex rule + # `location ~ /(\.|autotest|...)` which would otherwise handle requests + # for `/.well-known`. + location ^~ /.well-known { + # The rules in this block are an adaptation of the rules + # in `.htaccess` that concern `/.well-known`. + + location = /.well-known/carddav { return 301 /remote.php/dav/; } + location = /.well-known/caldav { return 301 /remote.php/dav/; } + + location /.well-known/acme-challenge { try_files $uri $uri/ =404; } + location /.well-known/pki-validation { try_files $uri $uri/ =404; } + + # Let Nextcloud's API for `/.well-known` URIs handle all other + # requests by passing them to the front-end controller. + return 301 /index.php$request_uri; + } + + # Rules borrowed from `.htaccess` to hide certain paths from clients + location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; } + location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; } + + # Ensure this block, which passes PHP files to the PHP process, is above the blocks + # which handle static assets (as seen below). If this block is not declared first, + # then Nginx will encounter an infinite rewriting loop when it prepends `/index.php` + # to the URI, resulting in a HTTP 500 error response. + location ~ \.php(?:$|/) { + # Required for legacy support + rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode(_arm64)?\/proxy) /index.php$request_uri; + + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + set $path_info $fastcgi_path_info; + + try_files $fastcgi_script_name =404; + + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $path_info; + fastcgi_param HTTPS on; + + fastcgi_param modHeadersAvailable true; # Avoid sending the security headers twice + fastcgi_param front_controller_active true; # Enable pretty urls + fastcgi_pass php-handler; + + fastcgi_intercept_errors on; + fastcgi_request_buffering off; + + fastcgi_max_temp_file_size 0; + } + + # Serve static files + location ~ \.(?:css|js|mjs|svg|gif|ico|jpg|png|webp|wasm|tflite|map|ogg|flac)$ { + try_files $uri /index.php$request_uri; + # HTTP response headers borrowed from Nextcloud `.htaccess` + add_header Cache-Control "public, max-age=15778463$asset_immutable"; + add_header Referrer-Policy "no-referrer" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Permitted-Cross-Domain-Policies "none" always; + add_header X-Robots-Tag "noindex, nofollow" always; + add_header X-XSS-Protection "1; mode=block" always; + access_log off; # Optional: Don't log access to assets + } + + location ~ \.(otf|woff2?)$ { + try_files $uri /index.php$request_uri; + expires 7d; # Cache-Control policy borrowed from `.htaccess` + access_log off; # Optional: Don't log access to assets + } + + # Rule borrowed from `.htaccess` + location /remote { + return 301 /remote.php$request_uri; + } + + location / { + try_files $uri $uri/ /index.php$request_uri; + } + } + + # another virtual host using mix of IP-, name-, and port-based configuration + # + #server { + # listen 8000; + # listen somename:8080; + # server_name somename alias another.alias; + + # location / { + # root html; + # index index.html index.htm; + # } + #} + + + # HTTPS server + # + #server { + # listen 443 ssl; + # server_name localhost; + + # ssl_certificate cert.pem; + # ssl_certificate_key cert.key; + + # ssl_session_cache shared:SSL:1m; + # ssl_session_timeout 5m; + + # ssl_ciphers HIGH:!aNULL:!MD5; + # ssl_prefer_server_ciphers on; + + # location / { + # root html; + # index index.html index.htm; + # } + #} + +} diff --git a/containers/Bastille/Postgres.md b/containers/Bastille/Postgres.md new file mode 100644 index 0000000..ddd64c6 --- /dev/null +++ b/containers/Bastille/Postgres.md @@ -0,0 +1,54 @@ +# Postgres + +```sh +bastille create postgresql 14.2-RELEASE 192.168.1.203 em0 +bastille config postgresql set allow.sysvipc=1 +bastille restart postgresql +bastille pkg postgresql bootstrap +bastille pkg postgresql update +bastille pkg postgresql install -y postgresql15-server postgresql15-client +bastille service postgresql postgresql enable +bastille service postgresql postgresql initdb +bastille service postgresql postgresql start +``` + +You need to change `/var/db/postgres/data15/postgresql.conf` + +```sh +nvim /var/db/postgres/data15/postgresql.conf +``` + +To listen to the ip address: + +``` +listen_addresses = '192.168.1.203' +``` + +And restart. + +We need to allow communications via the jails. Add this to pf.conf on the host: + +``` +pass in on $ext_if proto tcp from 192.168.1.202 to 192.168.1.203 port 5432 +pass out on $ext_if proto tcp from 192.168.1.203 to 192.168.1.202 port 5432 +``` + +Add a user, for example nextcloud: + +```sh +su - postgres +createuser nextcloud +createdb nextcloud -O admin +psql nextcloud +alter role nextcloud with encrypted password 'yourpassword'; +grant all privileges on database nextcloud to nextcloud; +exit +exit +``` + +Add this to `/var/db/postgres/data15/pg_hba.conf` + +``` +host nextcloud nextcloud 0.0.0.0/0 scram-sha-256 +host nextcloud nextcloud ::/0 scram-sha-256 +``` diff --git a/containers/Bastille/nginx.md b/containers/Bastille/nginx.md new file mode 100644 index 0000000..a6f75e8 --- /dev/null +++ b/containers/Bastille/nginx.md @@ -0,0 +1,10 @@ +# nginx + +```sh +bastille create nginx 14.2-RELEASE 192.168.1.200 genet0 +bastille pkg nginx bootstrap +bastille pkg nginx update +bastille pkg nginx install -y nginx +bastille service nginx nginx enable +bastille service nginx nginx start +``` diff --git a/containers/docker/audiobookshelf/compose.yaml b/containers/docker/audiobookshelf/compose.yaml new file mode 100644 index 0000000..c99d15a --- /dev/null +++ b/containers/docker/audiobookshelf/compose.yaml @@ -0,0 +1,13 @@ +services: + audiobookshelf: + image: ghcr.io/advplyr/audiobookshelf:latest + # user: 1004:1004 + ports: + - 13378:80 + volumes: + - /home/audiobookshelf/audiobookshelf/audiobooks:/audiobooks + - /home/audiobookshelf/audiobookshelf/podcasts:/podcasts + - /home/audiobookshelf/audiobookshelf/config:/config + - /home/audiobookshelf/audiobookshelf/metadata:/metadata + environment: + - TZ=Europe/Amsterdam diff --git a/containers/docker/httpd/compose.yaml b/containers/docker/httpd/compose.yaml new file mode 100644 index 0000000..9da83c0 --- /dev/null +++ b/containers/docker/httpd/compose.yaml @@ -0,0 +1,9 @@ +services: + + httpd: + image: httpd + restart: always + ports: + - 8081:80 + # volumes: + # - ./www:/usr/local/apache2/htdocs diff --git a/containers/docker/httpd/www/index.html b/containers/docker/httpd/www/index.html new file mode 100644 index 0000000..7f7697a --- /dev/null +++ b/containers/docker/httpd/www/index.html @@ -0,0 +1,8 @@ + + + Hello world! + + +

Hello world!

+ + \ No newline at end of file diff --git a/containers/docker/immich/compose.yaml b/containers/docker/immich/compose.yaml new file mode 100644 index 0000000..f16611f --- /dev/null +++ b/containers/docker/immich/compose.yaml @@ -0,0 +1,84 @@ +# You need to get the example .env file: +# wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env + +name: immich + +services: + immich-server: + container_name: immich_server + image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release} + extends: + file: hwaccel.transcoding.yml + service: quicksync # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding + volumes: + # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file + - ${UPLOAD_LOCATION}:/usr/src/app/upload + - /etc/localtime:/etc/localtime:ro + - /home/immich/external/:/external + env_file: + - .env + ports: + - '2283:2283' + depends_on: + - redis + - database + restart: always + healthcheck: + disable: false + + immich-machine-learning: + container_name: immich_machine_learning + # For hardware acceleration, add one of -[armnn, cuda, openvino] to the image tag. + # Example tag: ${IMMICH_VERSION:-release}-cuda + image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release} + # extends: # uncomment this section for hardware acceleration - see https://immich.app/docs/features/ml-hardware-acceleration + # file: hwaccel.ml.yml + # service: cpu # set to one of [armnn, cuda, openvino, openvino-wsl] for accelerated inference - use the `-wsl` version for WSL2 where applicable + volumes: + - model-cache:/cache + env_file: + - .env + restart: always + healthcheck: + disable: false + + redis: + container_name: immich_redis + image: docker.io/redis:6.2-alpine@sha256:eaba718fecd1196d88533de7ba49bf903ad33664a92debb24660a922ecd9cac8 + healthcheck: + test: redis-cli ping || exit 1 + restart: always + + database: + container_name: immich_postgres + image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0 + environment: + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_USER: ${DB_USERNAME} + POSTGRES_DB: ${DB_DATABASE_NAME} + POSTGRES_INITDB_ARGS: '--data-checksums' + volumes: + # Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file + - ${DB_DATA_LOCATION}:/var/lib/postgresql/data + healthcheck: + test: >- + pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1; + Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align + --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; + echo "checksum failure count is $$Chksum"; + [ "$$Chksum" = '0' ] || exit 1 + interval: 5m + start_interval: 30s + start_period: 5m + command: >- + postgres + -c shared_preload_libraries=vectors.so + -c 'search_path="$$user", public, vectors' + -c logging_collector=on + -c max_wal_size=2GB + -c shared_buffers=512MB + -c wal_compression=on + restart: always + +volumes: + model-cache: diff --git a/containers/docker/immich/hwaccel.transcoding.yml b/containers/docker/immich/hwaccel.transcoding.yml new file mode 100644 index 0000000..33fb7b3 --- /dev/null +++ b/containers/docker/immich/hwaccel.transcoding.yml @@ -0,0 +1,54 @@ +# Configurations for hardware-accelerated transcoding + +# If using Unraid or another platform that doesn't allow multiple Compose files, +# you can inline the config for a backend by copying its contents +# into the immich-microservices service in the docker-compose.yml file. + +# See https://immich.app/docs/features/hardware-transcoding for more info on using hardware transcoding. + +services: + cpu: {} + + nvenc: + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: + - gpu + - compute + - video + + quicksync: + devices: + - /dev/dri:/dev/dri + + rkmpp: + security_opt: # enables full access to /sys and /proc, still far better than privileged: true + - systempaths=unconfined + - apparmor=unconfined + group_add: + - video + devices: + - /dev/rga:/dev/rga + - /dev/dri:/dev/dri + - /dev/dma_heap:/dev/dma_heap + - /dev/mpp_service:/dev/mpp_service + #- /dev/mali0:/dev/mali0 # only required to enable OpenCL-accelerated HDR -> SDR tonemapping + volumes: + #- /etc/OpenCL:/etc/OpenCL:ro # only required to enable OpenCL-accelerated HDR -> SDR tonemapping + #- /usr/lib/aarch64-linux-gnu/libmali.so.1:/usr/lib/aarch64-linux-gnu/libmali.so.1:ro # only required to enable OpenCL-accelerated HDR -> SDR tonemapping + + vaapi: + devices: + - /dev/dri:/dev/dri + + vaapi-wsl: # use this for VAAPI if you're running Immich in WSL2 + devices: + - /dev/dri:/dev/dri + volumes: + - /usr/lib/wsl:/usr/lib/wsl + environment: + - LIBVA_DRIVER_NAME=d3d12 diff --git a/containers/docker/jellyfin/compose.yaml b/containers/docker/jellyfin/compose.yaml new file mode 100644 index 0000000..25a3691 --- /dev/null +++ b/containers/docker/jellyfin/compose.yaml @@ -0,0 +1,30 @@ +services: + jellyfin: + image: jellyfin/jellyfin + container_name: jellyfin + user: 1003:1003 + network_mode: 'host' + volumes: + - /home/jellyfin/jellyfin/config:/config + - /home/jellyfin/jellyfin/cache:/cache + - type: bind + source: /home/jellyfin/jellyfin/media + target: /media + # - type: bind + # source: /path/to/media2 + # target: /media2 + # read_only: true + # Optional - extra fonts to be used during transcoding with subtitle burn-in + # - type: bind + # source: /path/to/fonts + # target: /usr/local/share/fonts/custom + # read_only: true + restart: 'unless-stopped' + # Optional - alternative address used for autodiscovery + # environment: + # - JELLYFIN_PublishedServerUrl=http://example.com + # Optional - may be necessary for docker healthcheck to pass if running in host network mode + # extra_hosts: + # - 'host.docker.internal:host-gateway' + devices: + - "/dev/dri:/dev/dri" # Intel QSV \ No newline at end of file diff --git a/containers/docker/multitenant/.gitignore b/containers/docker/multitenant/.gitignore new file mode 100644 index 0000000..293d9b7 --- /dev/null +++ b/containers/docker/multitenant/.gitignore @@ -0,0 +1,3 @@ +infra/nginx-proxy-manager_data/ +infra/portainer_data/ +lemmy/volumes/ diff --git a/containers/docker/multitenant/README.md b/containers/docker/multitenant/README.md new file mode 100644 index 0000000..3f7020e --- /dev/null +++ b/containers/docker/multitenant/README.md @@ -0,0 +1,55 @@ +# multitenant + +## nginx-proxy-manager + +```sh +docker network create infra +``` + +Admin: http://127.0.0.1:81/ + +``` +admin@example.com +changeme +``` + +## Wordpress + +We need to add domain names in our `/etc/hosts` file: + +``` +127.0.0.1 moni0.codecompost.nl +127.0.0.1 moni1.codecompost.nl +``` + +And pass a project name to the docker-compose command: + +``` +docker-compose -p moni0 up --build --force-recreate -d +``` + +This will prepend `moni0_` to the containers (and add a `_1` apparently) + +``` +Creating network "moni0_internal_network" with the default driver +Creating volume "moni0_wordpress" with default driver +Creating volume "moni0_db" with default driver +Creating moni0_db_1 ... done +Creating moni0_wordpress_1 ... done +``` + +In the Nginx Proxy Manager, we can add the proxy host: + +![moni0 wordpress proxy host](moni0_proxy_host.png) + +We can go ahead and start a second wordpress with: + +``` +docker-compose -p moni1 up --build --force-recreate -d +``` + +And then you can configure it like this: + +![Alt text](moni1_proxy_host.png) + +Make sure that `moni1.codecompost.nl` is also in your hosts file, otherwise it won't work! \ No newline at end of file diff --git a/containers/docker/multitenant/infra/docker-compose.yml b/containers/docker/multitenant/infra/docker-compose.yml new file mode 100644 index 0000000..040df87 --- /dev/null +++ b/containers/docker/multitenant/infra/docker-compose.yml @@ -0,0 +1,31 @@ +version: '3.8' +services: + + nginx-proxy-manager: + image: 'jc21/nginx-proxy-manager:latest' + restart: unless-stopped + ports: + - '80:80' + - '81:81' + - '443:443' + volumes: + - ./nginx-proxy-manager_data/data:/data + - ./nginx-proxy-manager_data/letsencrypt:/etc/letsencrypt + networks: + - infra + +# portainer: +# image: 'portainer/portainer-ce:latest' +# restart: always +# ports: +# - '8000:8000' +# - '9443:9443' +# volumes: +# - /var/run/docker.sock:/var/run/docker.sock +# - ./portainer_data:/data +# networks: +# - infra + +networks: + infra: + external: true diff --git a/containers/docker/multitenant/jellyfin/run_with_podman.sh b/containers/docker/multitenant/jellyfin/run_with_podman.sh new file mode 100755 index 0000000..d521033 --- /dev/null +++ b/containers/docker/multitenant/jellyfin/run_with_podman.sh @@ -0,0 +1,12 @@ +podman run \ + --detach \ + --label "io.containers.autoupdate=registry" \ + --name jellyfin \ + --publish 8096:8096/tcp \ + --rm \ + --user $(id -u):$(id -g) \ + --userns keep-id \ + --volume jellyfin-cache:/cache:Z \ + --volume jellyfin-config:/config:Z \ + --mount type=bind,source=/home/moni/media/,destination=/media,ro=true,relabel=private \ + docker.io/jellyfin/jellyfin:latest \ No newline at end of file diff --git a/containers/docker/multitenant/lemmy/customPostgresql.conf b/containers/docker/multitenant/lemmy/customPostgresql.conf new file mode 100644 index 0000000..49428e4 --- /dev/null +++ b/containers/docker/multitenant/lemmy/customPostgresql.conf @@ -0,0 +1,30 @@ +# DB Version: 15 +# OS Type: linux +# DB Type: web +# Total Memory (RAM): 8 GB +# CPUs num: 4 +# Data Storage: ssd + +max_connections = 200 +shared_buffers = 2GB +effective_cache_size = 6GB +maintenance_work_mem = 512MB +checkpoint_completion_target = 0.9 +checkpoint_timeout = 86400 +wal_buffers = 16MB +default_statistics_target = 100 +random_page_cost = 1.1 +effective_io_concurrency = 200 +work_mem = 5242kB +min_wal_size = 1GB +max_wal_size = 30GB +max_worker_processes = 4 +max_parallel_workers_per_gather = 2 +max_parallel_workers = 4 +max_parallel_maintenance_workers = 2 + +# Other custom params +temp_file_size=1GB +synchronous_commit=off +# This one shouldn't be on regularly, because DB migrations often take a long time +# statement_timeout = 10000 diff --git a/containers/docker/multitenant/lemmy/docker-compose.yml b/containers/docker/multitenant/lemmy/docker-compose.yml new file mode 100644 index 0000000..b6b2f62 --- /dev/null +++ b/containers/docker/multitenant/lemmy/docker-compose.yml @@ -0,0 +1,114 @@ +version: "3.7" + +x-logging: &default-logging + driver: "json-file" + options: + max-size: "50m" + max-file: "4" + +services: + proxy: + image: nginx:1-alpine + # ports: + # # actual and only port facing any connection from outside + # # Note, change the left number if port 1236 is already in use on your system + # # You could use port 80 if you won't use a reverse proxy + # - "1236:8536" + volumes: + - ./nginx_internal.conf:/etc/nginx/nginx.conf:ro,Z + restart: always + logging: *default-logging + depends_on: + - pictrs + - lemmy-ui + networks: + - lemmy-internal + - infra + + lemmy: + image: dessalines/lemmy:latest + hostname: lemmy + restart: always + logging: *default-logging + environment: + - RUST_LOG="warn" + - LEMMY_CORS_ORIGIN=http://lemmy.codecompost.nl + volumes: + - ./lemmy.hjson:/config/config.hjson:Z + depends_on: + - postgres + - pictrs + networks: + - lemmy-internal + + lemmy-ui: + image: dessalines/lemmy-ui:latest + environment: + - LEMMY_UI_LEMMY_INTERNAL_HOST=lemmy:8536 + - LEMMY_UI_LEMMY_EXTERNAL_HOST=lemmy.codecompost.nl + - LEMMY_UI_HTTPS=true + volumes: + - ./volumes/lemmy-ui/extra_themes:/app/extra_themes + depends_on: + - lemmy + restart: always + logging: *default-logging + networks: + - lemmy-internal + + pictrs: + image: asonix/pictrs:0.4.0-rc.7 + # this needs to match the pictrs url in lemmy.hjson + hostname: pictrs + # we can set options to pictrs like this, here we set max. image size and forced format for conversion + # entrypoint: /sbin/tini -- /usr/local/bin/pict-rs -p /mnt -m 4 --image-format webp + environment: + - PICTRS_OPENTELEMETRY_URL=http://otel:4137 + - PICTRS__API_KEY=JmT3cMDL252EJw + - RUST_LOG=debug + - RUST_BACKTRACE=full + - PICTRS__MEDIA__VIDEO_CODEC=vp9 + - PICTRS__MEDIA__GIF__MAX_WIDTH=256 + - PICTRS__MEDIA__GIF__MAX_HEIGHT=256 + - PICTRS__MEDIA__GIF__MAX_AREA=65536 + - PICTRS__MEDIA__GIF__MAX_FRAME_COUNT=400 + user: 991:991 + volumes: + - ./volumes/pictrs:/mnt:Z + restart: always + logging: *default-logging + deploy: + resources: + limits: + memory: 690m + networks: + - lemmy-internal + + postgres: + image: postgres:15-alpine + hostname: postgres + environment: + - POSTGRES_USER=lemmy + - POSTGRES_PASSWORD=JmT3cMDL252EJw + - POSTGRES_DB=lemmy + volumes: + - ./volumes/postgres:/var/lib/postgresql/data:Z + - ./customPostgresql.conf:/etc/postgresql.conf + restart: always + logging: *default-logging + networks: + - lemmy-internal + + postfix: + image: mwader/postfix-relay + environment: + - POSTFIX_myhostname=codecompost.nl + restart: "always" + logging: *default-logging + networks: + - lemmy-internal + +networks: + lemmy-internal: + infra: + external: true \ No newline at end of file diff --git a/containers/docker/multitenant/lemmy/lemmy.hjson b/containers/docker/multitenant/lemmy/lemmy.hjson new file mode 100644 index 0000000..a27c04e --- /dev/null +++ b/containers/docker/multitenant/lemmy/lemmy.hjson @@ -0,0 +1,19 @@ +{ + # for more info about the config, check out the documentation + # https://join-lemmy.org/docs/en/administration/configuration.html + + database: { + host: postgres + password: "JmT3cMDL252EJw" + } + hostname: "lemmy.codecompost.nl" + pictrs: { + url: "http://pictrs:8080/" + api_key: "JmT3cMDL252EJw" + } + email: { + smtp_server: "postfix:25" + smtp_from_address: "noreply@codecompost.nl" + tls_type: "none" + } +} diff --git a/containers/docker/multitenant/lemmy/nginx_internal.conf b/containers/docker/multitenant/lemmy/nginx_internal.conf new file mode 100644 index 0000000..cffc77b --- /dev/null +++ b/containers/docker/multitenant/lemmy/nginx_internal.conf @@ -0,0 +1,96 @@ +worker_processes auto; + +events { + worker_connections 1024; +} + +http { + # We construct a string consistent of the "request method" and "http accept header" + # and then apply soem ~simply regexp matches to that combination to decide on the + # HTTP upstream we should proxy the request to. + # + # Example strings: + # + # "GET:application/activity+json" + # "GET:text/html" + # "POST:application/activity+json" + # + # You can see some basic match tests in this regex101 matching this configuration + # https://regex101.com/r/vwMJNc/1 + # + # Learn more about nginx maps here http://nginx.org/en/docs/http/ngx_http_map_module.html + map "$request_method:$http_accept" $proxpass { + # If no explicit matches exists below, send traffic to lemmy-ui + default "http://lemmy-ui"; + + # GET/HEAD requests that accepts ActivityPub or Linked Data JSON should go to lemmy. + # + # These requests are used by Mastodon and other fediverse instances to look up profile information, + # discover site information and so on. + "~^(?:GET|HEAD):.*?application\/(?:activity|ld)\+json" "http://lemmy"; + + # All non-GET/HEAD requests should go to lemmy + # + # Rather than calling out POST, PUT, DELETE, PATCH, CONNECT and all the verbs manually + # we simply negate the GET|HEAD pattern from above and accept all possibly $http_accept values + "~^(?!(GET|HEAD)).*:" "http://lemmy"; + } + + upstream lemmy { + # this needs to map to the lemmy (server) docker service hostname + server "lemmy:8536"; + } + + upstream lemmy-ui { + # this needs to map to the lemmy-ui docker service hostname + server "lemmy-ui:1234"; + } + + server { + # this is the port inside docker, not the public one yet + listen 1236; + listen 8536; + + # change if needed, this is facing the public web + server_name localhost; + server_tokens off; + + gzip on; + gzip_types text/css application/javascript image/svg+xml; + gzip_vary on; + + # Upload limit, relevant for pictrs + client_max_body_size 20M; + + add_header X-Frame-Options SAMEORIGIN; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + + # frontend general requests + location / { + proxy_pass $proxpass; + + rewrite ^(.+)/+$ $1 permanent; + + # Send actual client IP upstream + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + # backend + location ~ ^/(api|pictrs|feeds|nodeinfo|.well-known) { + proxy_pass "http://lemmy"; + + # proxy common stuff + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # Send actual client IP upstream + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + } +} diff --git a/containers/docker/multitenant/mastodon/.env.production b/containers/docker/multitenant/mastodon/.env.production new file mode 100644 index 0000000..93a732c --- /dev/null +++ b/containers/docker/multitenant/mastodon/.env.production @@ -0,0 +1,28 @@ +# Generated with mastodon:setup on 2023-07-06 02:48:24 UTC + +# Some variables in this file will be interpreted differently whether you are +# using docker-compose or not. + +LOCAL_DOMAIN=mastodon.codecompost.nl +SINGLE_USER_MODE=true +SECRET_KEY_BASE=2f845c9336267ad5ddf79ca900d3940990c0a8010c7c3a5321d5cda8e5b9ac32ebcc75ea67396977bb65c7c970f71d123312cd180c6a1e7b12e608eee3d63e27 +OTP_SECRET=e8c4370e30704c85552fd48d766c859323ebd28aaaffa8a9943687b2ac925610eeba1bc9b3a472ae1b684862e7d02ea7d5b69a02624d395626b0cd9fef4aeb2c +VAPID_PRIVATE_KEY=fHCAaeeQ_HxQBKvw9jzVrTxO9mpoup46luhPmKZsRhI= +VAPID_PUBLIC_KEY=BKkmTVZwiL4hmqYt91AsWvB4DS38MnFygDumi9jCqjSw3IClXA5WhxCQnYSpGzOKvvrN-Lgy4Fm2Rx5CPhmelS0= +DB_HOST=moni-mastodon-db-1 +DB_PORT=5432 +DB_NAME=mastodon +DB_USER=mastodon +DB_PASS=mastodon +REDIS_HOST=moni-mastodon-redis-1 +REDIS_PORT=6379 +REDIS_PASSWORD= +SMTP_SERVER=smtp.mailgun.org +SMTP_PORT=587 +SMTP_LOGIN= +SMTP_PASSWORD= +SMTP_AUTH_METHOD=plain +SMTP_OPENSSL_VERIFY_MODE=none +SMTP_ENABLE_STARTTLS=auto +SMTP_FROM_ADDRESS=Mastodon +LOCAL_HTTPS=false diff --git a/containers/docker/multitenant/mastodon/docker-compose.yml b/containers/docker/multitenant/mastodon/docker-compose.yml new file mode 100644 index 0000000..a3916ba --- /dev/null +++ b/containers/docker/multitenant/mastodon/docker-compose.yml @@ -0,0 +1,137 @@ +version: '3' +services: + db: + restart: always + image: postgres:14-alpine + shm_size: 256mb + networks: + - internal_network + healthcheck: + test: ['CMD', 'pg_isready', '-U', 'postgres'] + volumes: + - postgres14:/var/lib/postgresql/data + environment: + - POSTGRES_USER=mastodon + - POSTGRES_PASSWORD=mastodon + + redis: + restart: always + image: redis:7-alpine + networks: + - internal_network + healthcheck: + test: ['CMD', 'redis-cli', 'ping'] + volumes: + - redis:/data + + # es: + # restart: always + # image: docker.elastic.co/elasticsearch/elasticsearch:7.17.4 + # environment: + # - "ES_JAVA_OPTS=-Xms512m -Xmx512m -Des.enforce.bootstrap.checks=true" + # - "xpack.license.self_generated.type=basic" + # - "xpack.security.enabled=false" + # - "xpack.watcher.enabled=false" + # - "xpack.graph.enabled=false" + # - "xpack.ml.enabled=false" + # - "bootstrap.memory_lock=true" + # - "cluster.name=es-mastodon" + # - "discovery.type=single-node" + # - "thread_pool.write.queue_size=1000" + # networks: + # - infra + # - internal_network + # healthcheck: + # test: ["CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1"] + # volumes: + # - ./elasticsearch:/usr/share/elasticsearch/data + # ulimits: + # memlock: + # soft: -1 + # hard: -1 + # nofile: + # soft: 65536 + # hard: 65536 + # ports: + # - '127.0.0.1:9200:9200' + + web: + image: ghcr.io/mastodon/mastodon + restart: always + env_file: .env.production + command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000" + networks: + - infra + - internal_network + healthcheck: + # prettier-ignore + test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:3000/health || exit 1'] + # ports: + # - '127.0.0.1:3000:3000' + depends_on: + - db + - redis + # - es + volumes: + - public_system:/mastodon/public/system + + streaming: + image: ghcr.io/mastodon/mastodon + restart: always + env_file: .env.production + command: node ./streaming + networks: + - infra + - internal_network + healthcheck: + # prettier-ignore + test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1'] + # ports: + # - '127.0.0.1:4000:4000' + depends_on: + - db + - redis + + sidekiq: + image: ghcr.io/mastodon/mastodon + restart: always + env_file: .env.production + command: bundle exec sidekiq + depends_on: + - db + - redis + networks: + - infra + - internal_network + volumes: + - public_system:/mastodon/public/system + healthcheck: + test: ['CMD-SHELL', "ps aux | grep '[s]idekiq\ 6' || false"] + + ## Uncomment to enable federation with tor instances along with adding the following ENV variables + ## http_proxy=http://privoxy:8118 + ## ALLOW_ACCESS_TO_HIDDEN_SERVICE=true + # tor: + # image: sirboops/tor + # networks: + # - infra + # - internal_network + # + # privoxy: + # image: sirboops/privoxy + # volumes: + # - ./priv-config:/opt/config + # networks: + # - infra + # - internal_network + +networks: + infra: + external: true + internal_network: + + +volumes: + postgres14: + redis: + public_system: diff --git a/containers/docker/multitenant/moni0_proxy_host.png b/containers/docker/multitenant/moni0_proxy_host.png new file mode 100644 index 0000000000000000000000000000000000000000..94903a182e13f86fb6051e30fcfcd4518f498372 GIT binary patch literal 46430 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

9c(&Gt+3QyAqICh&z7{}m4D;SdrUOSE5K`*F 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 z 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 B&Km## literal 0 HcmV?d00001 diff --git a/containers/docker/multitenant/nextcloud/REAME.md b/containers/docker/multitenant/nextcloud/REAME.md new file mode 100644 index 0000000..0e87b2c --- /dev/null +++ b/containers/docker/multitenant/nextcloud/REAME.md @@ -0,0 +1,2 @@ +https://github.com/nextcloud/all-in-one + diff --git a/containers/docker/multitenant/nextcloud/docker-compose.yml b/containers/docker/multitenant/nextcloud/docker-compose.yml new file mode 100644 index 0000000..e54a64c --- /dev/null +++ b/containers/docker/multitenant/nextcloud/docker-compose.yml @@ -0,0 +1,17 @@ +services: + nextcloud-aio-mastercontainer: + image: nextcloud/all-in-one:latest + init: true + restart: always + container_name: nextcloud-aio-mastercontainer # This line is not allowed to be changed as otherwise AIO will not work correctly + volumes: + - nextcloud_aio_mastercontainer:/mnt/docker-aio-config # This line is not allowed to be changed as otherwise the built-in backup solution will not work + - /var/run/docker.sock:/var/run/docker.sock:ro # May be changed on macOS, Windows or docker rootless. See the applicable documentation. If adjusting, don't forget to also set 'WATCHTOWER_DOCKER_SOCKET_PATH'! + ports: + - 80:80 # Can be removed when running behind a web server or reverse proxy (like Apache, Nginx, Cloudflare Tunnel and else). See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md + - 8080:8080 + - 8443:8443 + +volumes: # If you want to store the data on a different drive, see https://github.com/nextcloud/all-in-one#how-to-store-the-filesinstallation-on-a-separate-drive + nextcloud_aio_mastercontainer: + name: nextcloud_aio_mastercontainer # This line is not allowed to be changed as otherwise the built-in backup solution will not work diff --git a/containers/docker/multitenant/sites/moni8080/docker-compose.yml b/containers/docker/multitenant/sites/moni8080/docker-compose.yml new file mode 100644 index 0000000..4ccf31d --- /dev/null +++ b/containers/docker/multitenant/sites/moni8080/docker-compose.yml @@ -0,0 +1,12 @@ +version: '2' +services: + moni8080: + image: httpd + networks: + - npm + volumes: + - /home/moni/sites/moni8080:/usr/local/apache2/htdocs/ + +networks: + npm: + external: true \ No newline at end of file diff --git a/containers/docker/multitenant/sites/moni8080/index.html b/containers/docker/multitenant/sites/moni8080/index.html new file mode 100644 index 0000000..c04ca89 --- /dev/null +++ b/containers/docker/multitenant/sites/moni8080/index.html @@ -0,0 +1,8 @@ + + + Moni 8080 + + + This is MONI 8080 + + \ No newline at end of file diff --git a/containers/docker/multitenant/sites/moni8081/docker-compose.yml b/containers/docker/multitenant/sites/moni8081/docker-compose.yml new file mode 100644 index 0000000..4692c06 --- /dev/null +++ b/containers/docker/multitenant/sites/moni8081/docker-compose.yml @@ -0,0 +1,12 @@ +version: '2' +services: + moni8081: + image: httpd + networks: + - npm + volumes: + - /home/moni/sites/moni8081:/usr/local/apache2/htdocs/ + +networks: + npm: + external: true \ No newline at end of file diff --git a/containers/docker/multitenant/sites/moni8081/index.html b/containers/docker/multitenant/sites/moni8081/index.html new file mode 100644 index 0000000..aa79650 --- /dev/null +++ b/containers/docker/multitenant/sites/moni8081/index.html @@ -0,0 +1,8 @@ + + + Moni 8081 + + + This is MONI 8081 + + \ No newline at end of file diff --git a/containers/docker/multitenant/sites/moni8082/index.html b/containers/docker/multitenant/sites/moni8082/index.html new file mode 100644 index 0000000..7efe804 --- /dev/null +++ b/containers/docker/multitenant/sites/moni8082/index.html @@ -0,0 +1,8 @@ + + + Moni 8082 + + + This is MONI 8082 + + \ No newline at end of file diff --git a/containers/docker/multitenant/wordpress/Dockerfile b/containers/docker/multitenant/wordpress/Dockerfile new file mode 100644 index 0000000..3fe8b2d --- /dev/null +++ b/containers/docker/multitenant/wordpress/Dockerfile @@ -0,0 +1,7 @@ +# Not the latest version to be able to test updates +FROM wordpress:6.0.0 +WORKDIR /usr/src/wordpress +RUN set -eux; \ + find /etc/apache2 -name '*.conf' -type f -exec sed -ri -e "s!/var/www/html!$PWD!g" -e "s!Directory /var/www/!Directory $PWD!g" '{}' +; \ + cp -s wp-config-docker.php wp-config.php +RUN echo "define('FS_METHOD','direct');" >> wp-config.php \ No newline at end of file diff --git a/containers/docker/multitenant/wordpress/docker-compose.yml b/containers/docker/multitenant/wordpress/docker-compose.yml new file mode 100644 index 0000000..ab9c4fe --- /dev/null +++ b/containers/docker/multitenant/wordpress/docker-compose.yml @@ -0,0 +1,43 @@ +version: '3.1' + +services: + + wordpress: + image: wordpress + restart: always + # ports: + # - 8080:80 + depends_on: + - db + environment: + WORDPRESS_DB_HOST: db + WORDPRESS_DB_USER: exampleuser + WORDPRESS_DB_PASSWORD: examplepass + WORDPRESS_DB_NAME: exampledb + volumes: + - wordpress:/var/www/html + networks: + - infra + - internal_network + + db: + image: mysql:5.7 + restart: always + environment: + MYSQL_DATABASE: exampledb + MYSQL_USER: exampleuser + MYSQL_PASSWORD: examplepass + MYSQL_RANDOM_ROOT_PASSWORD: '1' + volumes: + - db:/var/lib/mysql + networks: + - internal_network + +volumes: + wordpress: + db: + +networks: + infra: + external: true + internal_network: diff --git a/containers/docker/nextcloud/README.md b/containers/docker/nextcloud/README.md new file mode 100644 index 0000000..83c4509 --- /dev/null +++ b/containers/docker/nextcloud/README.md @@ -0,0 +1,40 @@ +# Disabling SSL + +```sh +docker exec -it nextcloud-aio-mastercontainer bash +``` + +in the container: + +```sh +vi /etc/apache2/sites-available/mastercontainer.conf +``` + +Change the line to disable SSL: + +``` +SSLEngine off +``` + +Restart https *within the container*: + +```sh +killall httpd +/usr/sbin/httpd +``` + +You can exit after that. + +# Run OCC command + +```sh +docker exec -it --user www-data nextcloud-aio-nextcloud /var/www/html/occ +``` + +# Brute force protection + +To add the Caddy Jails container that is running on `192.168.1.200` to the trusted proxies list: + +```sh +docker exec --user www-data -it nextcloud-aio-nextcloud php occ config:system:set trusted_proxies 2 --value="192.168.1.200" +``` diff --git a/containers/docker/nextcloud/compose.yaml b/containers/docker/nextcloud/compose.yaml new file mode 100644 index 0000000..f730270 --- /dev/null +++ b/containers/docker/nextcloud/compose.yaml @@ -0,0 +1,67 @@ +services: + nextcloud-aio-mastercontainer: + image: nextcloud/all-in-one:latest + init: true + restart: always + container_name: nextcloud-aio-mastercontainer # This line is not allowed to be changed as otherwise AIO will not work correctly + volumes: + - nextcloud_aio_mastercontainer:/mnt/docker-aio-config # This line is not allowed to be changed as otherwise the built-in backup solution will not work + - /var/run/docker.sock:/var/run/docker.sock:ro # May be changed on macOS, Windows or docker rootless. See the applicable documentation. If adjusting, don't forget to also set 'WATCHTOWER_DOCKER_SOCKET_PATH'! + ports: + # - 80:80 # Can be removed when running behind a web server or reverse proxy (like Apache, Nginx, Cloudflare Tunnel and else). See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md + - 8080:8080 + # - 8443:8443 # Can be removed when running behind a web server or reverse proxy (like Apache, Nginx, Cloudflare Tunnel and else). See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md + environment: # Is needed when using any of the options below + # AIO_DISABLE_BACKUP_SECTION: false # Setting this to true allows to hide the backup section in the AIO interface. See https://github.com/nextcloud/all-in-one#how-to-disable-the-backup-section + APACHE_PORT: 11000 # Is needed when running behind a web server or reverse proxy (like Apache, Nginx, Cloudflare Tunnel and else). See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md + APACHE_IP_BINDING: 0.0.0.0 # Should be set when running behind a web server or reverse proxy (like Apache, Nginx, Cloudflare Tunnel and else) that is running on the same host. See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md + SKIP_DOMAIN_VALIDATION: true + ALLOW_INSECURE_ACCESS: true + # BORG_RETENTION_POLICY: --keep-within=7d --keep-weekly=4 --keep-monthly=6 # Allows to adjust borgs retention policy. See https://github.com/nextcloud/all-in-one#how-to-adjust-borgs-retention-policy + # COLLABORA_SECCOMP_DISABLED: false # Setting this to true allows to disable Collabora's Seccomp feature. See https://github.com/nextcloud/all-in-one#how-to-disable-collaboras-seccomp-feature + NEXTCLOUD_DATADIR: /home/nextcloud/nextcloud-data # Allows to set the host directory for Nextcloud's datadir. ⚠️⚠️⚠️ Warning: do not set or adjust this value after the initial Nextcloud installation is done! See https://github.com/nextcloud/all-in-one#how-to-change-the-default-location-of-nextclouds-datadir + # NEXTCLOUD_MOUNT: /mnt/ # Allows the Nextcloud container to access the chosen directory on the host. See https://github.com/nextcloud/all-in-one#how-to-allow-the-nextcloud-container-to-access-directories-on-the-host + # NEXTCLOUD_UPLOAD_LIMIT: 10G # Can be adjusted if you need more. See https://github.com/nextcloud/all-in-one#how-to-adjust-the-upload-limit-for-nextcloud + # NEXTCLOUD_MAX_TIME: 3600 # Can be adjusted if you need more. See https://github.com/nextcloud/all-in-one#how-to-adjust-the-max-execution-time-for-nextcloud + # NEXTCLOUD_MEMORY_LIMIT: 512M # Can be adjusted if you need more. See https://github.com/nextcloud/all-in-one#how-to-adjust-the-php-memory-limit-for-nextcloud + # NEXTCLOUD_TRUSTED_CACERTS_DIR: /path/to/my/cacerts # CA certificates in this directory will be trusted by the OS of the nexcloud container (Useful e.g. for LDAPS) See See https://github.com/nextcloud/all-in-one#how-to-trust-user-defined-certification-authorities-ca + # NEXTCLOUD_STARTUP_APPS: deck twofactor_totp tasks calendar contacts notes # Allows to modify the Nextcloud apps that are installed on starting AIO the first time. See https://github.com/nextcloud/all-in-one#how-to-change-the-nextcloud-apps-that-are-installed-on-the-first-startup + # NEXTCLOUD_ADDITIONAL_APKS: imagemagick # This allows to add additional packages to the Nextcloud container permanently. Default is imagemagick but can be overwritten by modifying this value. See https://github.com/nextcloud/all-in-one#how-to-add-os-packages-permanently-to-the-nextcloud-container + # NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS: imagick # This allows to add additional php extensions to the Nextcloud container permanently. Default is imagick but can be overwritten by modifying this value. See https://github.com/nextcloud/all-in-one#how-to-add-php-extensions-permanently-to-the-nextcloud-container + NEXTCLOUD_ENABLE_DRI_DEVICE: true # This allows to enable the /dev/dri device in the Nextcloud container. ⚠️⚠️⚠️ Warning: this only works if the '/dev/dri' device is present on the host! If it should not exist on your host, don't set this to true as otherwise the Nextcloud container will fail to start! See https://github.com/nextcloud/all-in-one#how-to-enable-hardware-transcoding-for-nextcloud + # NEXTCLOUD_KEEP_DISABLED_APPS: false # Setting this to true will keep Nextcloud apps that are disabled in the AIO interface and not uninstall them if they should be installed. See https://github.com/nextcloud/all-in-one#how-to-keep-disabled-apps + # TALK_PORT: 3478 # This allows to adjust the port that the talk container is using. See https://github.com/nextcloud/all-in-one#how-to-adjust-the-talk-port + # WATCHTOWER_DOCKER_SOCKET_PATH: /var/run/docker.sock # Needs to be specified if the docker socket on the host is not located in the default '/var/run/docker.sock'. Otherwise mastercontainer updates will fail. For macos it needs to be '/var/run/docker.sock' + # networks: # Is needed when you want to create the nextcloud-aio network with ipv6-support using this file, see the network config at the bottom of the file + # - nextcloud-aio # Is needed when you want to create the nextcloud-aio network with ipv6-support using this file, see the network config at the bottom of the file + # security_opt: ["label:disable"] # Is needed when using SELinux + + # # Optional: Caddy reverse proxy. See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md + # # You can find further examples here: https://github.com/nextcloud/all-in-one/discussions/588 + # caddy: + # image: caddy:alpine + # restart: always + # container_name: caddy + # volumes: + # - ./Caddyfile:/etc/caddy/Caddyfile + # - ./certs:/certs + # - ./config:/config + # - ./data:/data + # - ./sites:/srv + # network_mode: "host" + +volumes: # If you want to store the data on a different drive, see https://github.com/nextcloud/all-in-one#how-to-store-the-filesinstallation-on-a-separate-drive + nextcloud_aio_mastercontainer: + name: nextcloud_aio_mastercontainer # This line is not allowed to be changed as otherwise the built-in backup solution will not work + +# # Optional: If you need ipv6, follow step 1 and 2 of https://github.com/nextcloud/all-in-one/blob/main/docker-ipv6-support.md first and then uncomment the below config in order to activate ipv6 for the internal nextcloud-aio network. +# # Please make sure to uncomment also the networking lines of the mastercontainer above in order to actually create the network with docker-compose +# networks: +# nextcloud-aio: +# name: nextcloud-aio # This line is not allowed to be changed as otherwise the created network will not be used by the other containers of AIO +# driver: bridge +# enable_ipv6: true +# ipam: +# driver: default +# config: +# - subnet: fd12:3456:789a:2::/64 # IPv6 subnet to use diff --git a/containers/docker/nextcloud/restart.sh b/containers/docker/nextcloud/restart.sh new file mode 100755 index 0000000..356135d --- /dev/null +++ b/containers/docker/nextcloud/restart.sh @@ -0,0 +1,13 @@ +docker stop nextcloud-aio-apache && docker start nextcloud-aio-apache +docker stop nextcloud-aio-notify-push && docker start nextcloud-aio-notify-push +docker stop nextcloud-aio-nextcloud && docker start nextcloud-aio-nextcloud +# docker stop nextcloud-aio-docker-socket-proxy && docker start nextcloud-aio-docker-socket-proxy +docker stop nextcloud-aio-imaginary && docker start nextcloud-aio-imaginary +# docker stop nextcloud-aio-fulltextsearch && docker start nextcloud-aio-fulltextsearch +# docker stop nextcloud-aio-clamav && docker start nextcloud-aio-clamav +docker stop nextcloud-aio-redis && docker start nextcloud-aio-redis +docker stop nextcloud-aio-database && docker start nextcloud-aio-database +docker stop nextcloud-aio-talk && docker start nextcloud-aio-talk +docker stop nextcloud-aio-collabora && docker start nextcloud-aio-collabora +# docker stop nextcloud-aio-domaincheck # && docker start nextcloud-aio-domaincheck +# docker stop nextcloud-aio-mastercontainer && docker start nextcloud-aio-mastercontainer diff --git a/containers/docker/nextcloud/stop.sh b/containers/docker/nextcloud/stop.sh new file mode 100755 index 0000000..a297383 --- /dev/null +++ b/containers/docker/nextcloud/stop.sh @@ -0,0 +1,13 @@ +docker stop nextcloud-aio-apache +docker stop nextcloud-aio-notify-push +docker stop nextcloud-aio-nextcloud +# docker stop nextcloud-aio-docker-socket-proxy +docker stop nextcloud-aio-imaginary +# docker stop nextcloud-aio-fulltextsearch +# docker stop nextcloud-aio-clamav +docker stop nextcloud-aio-redis +docker stop nextcloud-aio-database +docker stop nextcloud-aio-talk +docker stop nextcloud-aio-collabora +# docker stop nextcloud-aio-domaincheck +# docker stop nextcloud-aio-mastercontainer diff --git a/containers/docker/photoprism/README.md b/containers/docker/photoprism/README.md new file mode 100644 index 0000000..adcee06 --- /dev/null +++ b/containers/docker/photoprism/README.md @@ -0,0 +1,3 @@ +```sh +docker compose exec photoprism photoprism --help +``` diff --git a/containers/docker/photoprism/docker-compose.yml b/containers/docker/photoprism/docker-compose.yml new file mode 100644 index 0000000..68c42ba --- /dev/null +++ b/containers/docker/photoprism/docker-compose.yml @@ -0,0 +1,152 @@ +# Example Docker Compose config file for PhotoPrism (Linux / AMD64) +# +# Note: +# - Running PhotoPrism on a server with less than 4 GB of swap space or setting a memory/swap limit can cause unexpected +# restarts ("crashes"), for example, when the indexer temporarily needs more memory to process large files. +# - If you install PhotoPrism on a public server outside your home network, please always run it behind a secure +# HTTPS reverse proxy such as Traefik or Caddy. Your files and passwords will otherwise be transmitted +# in clear text and can be intercepted by anyone, including your provider, hackers, and governments: +# https://docs.photoprism.app/getting-started/proxies/traefik/ +# +# Setup Guides: +# - https://docs.photoprism.app/getting-started/docker-compose/ +# - https://docs.photoprism.app/getting-started/raspberry-pi/ +# - https://www.photoprism.app/kb/activation +# +# Troubleshooting Checklists: +# - https://docs.photoprism.app/getting-started/troubleshooting/ +# - https://docs.photoprism.app/getting-started/troubleshooting/docker/ +# - https://docs.photoprism.app/getting-started/troubleshooting/mariadb/ +# +# CLI Commands: +# - https://docs.photoprism.app/getting-started/docker-compose/#command-line-interface +# +# All commands may have to be prefixed with "sudo" when not running as root. +# This will point the home directory shortcut ~ to /root in volume mounts. + +services: + photoprism: + ## Use photoprism/photoprism:preview for testing preview builds: + image: photoprism/photoprism:latest + ## Don't enable automatic restarts until PhotoPrism has been properly configured and tested! + ## If the service gets stuck in a restart loop, this points to a memory, filesystem, network, or database issue: + ## https://docs.photoprism.app/getting-started/troubleshooting/#fatal-server-errors + # restart: unless-stopped + stop_grace_period: 10s + depends_on: + - mariadb + security_opt: + - seccomp:unconfined + - apparmor:unconfined + ## Server port mapping in the format "Host:Container". To use a different port, change the host port on + ## the left-hand side and keep the container port, e.g. "80:2342" (for HTTP) or "443:2342 (for HTTPS): + ports: + - "2342:2342" + ## Before you start the service, please check the following config options (and change them as needed): + ## https://docs.photoprism.app/getting-started/config-options/ + environment: + PHOTOPRISM_ADMIN_USER: "admin" # admin login username + PHOTOPRISM_ADMIN_PASSWORD: "5bp3ptdBGM173t" # initial admin password (8-72 characters) + PHOTOPRISM_AUTH_MODE: "password" # authentication mode (public, password) + PHOTOPRISM_SITE_URL: "http://localhost:2342/" # server URL in the format "http(s)://domain.name(:port)/(path)" + PHOTOPRISM_DISABLE_TLS: "false" # disables HTTPS/TLS even if the site URL starts with https:// and a certificate is available + PHOTOPRISM_DEFAULT_TLS: "true" # defaults to a self-signed HTTPS/TLS certificate if no other certificate is available + PHOTOPRISM_ORIGINALS_LIMIT: 5000 # file size limit for originals in MB (increase for high-res video) + PHOTOPRISM_HTTP_COMPRESSION: "gzip" # improves transfer speed and bandwidth utilization (none or gzip) + PHOTOPRISM_LOG_LEVEL: "info" # log level: trace, debug, info, warning, error, fatal, or panic + PHOTOPRISM_READONLY: "false" # do not modify originals directory (reduced functionality) + PHOTOPRISM_EXPERIMENTAL: "false" # enables experimental features + PHOTOPRISM_DISABLE_CHOWN: "false" # disables updating storage permissions via chmod and chown on startup + PHOTOPRISM_DISABLE_WEBDAV: "false" # disables built-in WebDAV server + PHOTOPRISM_DISABLE_SETTINGS: "false" # disables settings UI and API + PHOTOPRISM_DISABLE_TENSORFLOW: "false" # disables all features depending on TensorFlow + PHOTOPRISM_DISABLE_FACES: "false" # disables face detection and recognition (requires TensorFlow) + PHOTOPRISM_DISABLE_CLASSIFICATION: "false" # disables image classification (requires TensorFlow) + PHOTOPRISM_DISABLE_VECTORS: "false" # disables vector graphics support + PHOTOPRISM_DISABLE_RAW: "false" # disables indexing and conversion of RAW images + PHOTOPRISM_RAW_PRESETS: "false" # enables applying user presets when converting RAW images (reduces performance) + PHOTOPRISM_SIDECAR_YAML: "true" # creates YAML sidecar files to back up picture metadata + PHOTOPRISM_BACKUP_ALBUMS: "true" # creates YAML files to back up album metadata + PHOTOPRISM_BACKUP_DATABASE: "true" # creates regular backups based on the configured schedule + PHOTOPRISM_BACKUP_SCHEDULE: "daily" # backup SCHEDULE in cron format (e.g. "0 12 * * *" for daily at noon) or at a random time (daily, weekly) + PHOTOPRISM_INDEX_SCHEDULE: "" # indexing SCHEDULE in cron format (e.g. "@every 3h" for every 3 hours; "" to disable) + PHOTOPRISM_AUTO_INDEX: 300 # delay before automatically indexing files in SECONDS when uploading via WebDAV (-1 to disable) + PHOTOPRISM_AUTO_IMPORT: -1 # delay before automatically importing files in SECONDS when uploading via WebDAV (-1 to disable) + PHOTOPRISM_DETECT_NSFW: "false" # automatically flags photos as private that MAY be offensive (requires TensorFlow) + PHOTOPRISM_UPLOAD_NSFW: "true" # allows uploads that MAY be offensive (no effect without TensorFlow) + # PHOTOPRISM_DATABASE_DRIVER: "sqlite" # SQLite is an embedded database that does not require a separate database server + PHOTOPRISM_DATABASE_DRIVER: "mysql" # MariaDB 10.5.12+ (MySQL successor) offers significantly better performance compared to SQLite + PHOTOPRISM_DATABASE_SERVER: "mariadb:3306" # MariaDB database server (hostname:port) + PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB database schema name + PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB database user name + PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB database user password + PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App" + PHOTOPRISM_SITE_DESCRIPTION: "" # meta site description + PHOTOPRISM_SITE_AUTHOR: "" # meta site author + ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/): + PHOTOPRISM_FFMPEG_ENCODER: "intel" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) + # PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840) + # PHOTOPRISM_FFMPEG_BITRATE: "32" # video bitrate limit in Mbit/s (default: 50) + ## Run/install on first startup (options: update https gpu ffmpeg tensorflow davfs clitools clean): + # PHOTOPRISM_INIT: "https gpu tensorflow" + ## Run as a non-root user after initialization (supported: 0, 33, 50-99, 500-600, and 900-1200): + # PHOTOPRISM_UID: 1000 + # PHOTOPRISM_GID: 1000 + # PHOTOPRISM_UMASK: 0000 + ## Start as non-root user before initialization (supported: 0, 33, 50-99, 500-600, and 900-1200): + # user: "1000:1000" + ## Share hardware devices with FFmpeg and TensorFlow (optional): + devices: + - "/dev/dri:/dev/dri" # Intel QSV + # - "/dev/nvidia0:/dev/nvidia0" # Nvidia CUDA + # - "/dev/nvidiactl:/dev/nvidiactl" + # - "/dev/nvidia-modeset:/dev/nvidia-modeset" + # - "/dev/nvidia-nvswitchctl:/dev/nvidia-nvswitchctl" + # - "/dev/nvidia-uvm:/dev/nvidia-uvm" + # - "/dev/nvidia-uvm-tools:/dev/nvidia-uvm-tools" + # - "/dev/video11:/dev/video11" # Video4Linux Video Encode Device (h264_v4l2m2m) + working_dir: "/photoprism" # do not change or remove + ## Storage Folders: "~" is a shortcut for your home directory, "." for the current directory + volumes: + # "/host/folder:/photoprism/folder" # Example + - "/home/photoprism/Pictures:/photoprism/originals" # Original media files (DO NOT REMOVE) + # - "/example/family:/photoprism/originals/family" # *Additional* media folders can be mounted like this + - "/home/photoprism/Import:/photoprism/import" # *Optional* base folder from which files can be imported to originals + - "/home/photoprism/storage:/photoprism/storage" # *Writable* storage folder for cache, database, and sidecar files (DO NOT REMOVE) + + ## MariaDB Database Server (recommended) + ## see https://docs.photoprism.app/getting-started/faq/#should-i-use-sqlite-mariadb-or-mysql + mariadb: + image: mariadb:11 + ## If MariaDB gets stuck in a restart loop, this points to a memory or filesystem issue: + ## https://docs.photoprism.app/getting-started/troubleshooting/#fatal-server-errors + restart: unless-stopped + stop_grace_period: 5s + security_opt: # see https://github.com/MariaDB/mariadb-docker/issues/434#issuecomment-1136151239 + - seccomp:unconfined + - apparmor:unconfined + command: --innodb-buffer-pool-size=512M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120 + ## Never store database files on an unreliable device such as a USB flash drive, an SD card, or a shared network folder: + volumes: + - "/home/photoprism/database:/var/lib/mysql" # DO NOT REMOVE + environment: + MARIADB_AUTO_UPGRADE: "1" + MARIADB_INITDB_SKIP_TZINFO: "1" + MARIADB_DATABASE: "photoprism" + MARIADB_USER: "photoprism" + MARIADB_PASSWORD: "insecure" + MARIADB_ROOT_PASSWORD: "insecure" + + ## Watchtower upgrades services automatically (optional) + ## see https://docs.photoprism.app/getting-started/updates/#watchtower + ## activate via "COMPOSE_PROFILES=update docker compose up -d" + watchtower: + restart: unless-stopped + image: containrrr/watchtower + profiles: ["update"] + environment: + WATCHTOWER_CLEANUP: "true" + WATCHTOWER_POLL_INTERVAL: 7200 # checks for updates every two hours + volumes: + - "/var/run/docker.sock:/var/run/docker.sock" + - "~/.docker/config.json:/config.json" # optional, for authentication if you have a Docker Hub account diff --git a/containers/docker/pinepods/compose.yaml b/containers/docker/pinepods/compose.yaml new file mode 100644 index 0000000..e53d1d9 --- /dev/null +++ b/containers/docker/pinepods/compose.yaml @@ -0,0 +1,31 @@ +name: pinepods + +services: + db: + image: postgres:latest + env_file: '.env' + volumes: + - /home/pinepods/pinepods/pgdata:/var/lib/postgresql/data + # Exposing the postgres database port is dumb. + # ports: + # - "5432:5432" + restart: always + + valkey: + image: valkey/valkey:8-alpine + # Exposing a nosql database is expecially dumb. + # ports: + # - "6379:6379" + + pinepods: + image: madeofpendletonwool/pinepods:latest + ports: + - "8040:8040" + env_file: '.env' + volumes: + # Mount the download and backup locations on the server + - /home/pinepods/pinepods/downloads:/opt/pinepods/downloads + - /home/pinepods/pinepods/backups:/opt/pinepods/backups + depends_on: + - db + - valkey \ No newline at end of file diff --git a/containers/docker/qbittorrent/docker-compose.yml b/containers/docker/qbittorrent/docker-compose.yml new file mode 100644 index 0000000..8dfae1b --- /dev/null +++ b/containers/docker/qbittorrent/docker-compose.yml @@ -0,0 +1,42 @@ +# https://github.com/nickkjolsing/dockerMullvadVPN + +name: torrent + +services: + openvpn-client: + image: ghcr.io/wfg/openvpn-client # Image on Docker. Shoutout to ghcr.io + container_name: openvpn-client + cap_add: + - NET_ADMIN # Needs to be here + environment: + - KILL_SWITCH=on # Turns off internet access if the VPN connection drops + - SUBNETS=192.168.0.0/24,192.168.1.0/24 + devices: + - /dev/net/tun + volumes: + - /home/jellyfin/mullvad_config_linux_nl_ams:/data/vpn + ports: + - 8082:8082 + - 6881:6881 + - 6881:6881/udp + restart: unless-stopped + + qbittorrent: + image: lscr.io/linuxserver/qbittorrent:latest + container_name: qbittorrent + environment: + - PUID=1003 + - PGID=1003 + - TZ=Europe/Amsterdam + - WEBUI_PORT=8082 + - TORRENTING_PORT=6881 + volumes: + - /home/jellyfin/qbitorrent/appdata:/config + - /home/jellyfin/qbitorrent/downloads:/downloads #optional + - /home/jellyfin/jellyfin/media:/media + # ports: + # - 8082:8082 + # - 6881:6881 + # - 6881:6881/udp + network_mode: container:openvpn-client # This uses the port setting of the openvpn + restart: unless-stopped diff --git a/containers/docker/webtop/compose.yaml b/containers/docker/webtop/compose.yaml new file mode 100644 index 0000000..4236359 --- /dev/null +++ b/containers/docker/webtop/compose.yaml @@ -0,0 +1,24 @@ +services: + webtop: + image: lscr.io/linuxserver/webtop:fedora-xfce + container_name: webtop + security_opt: + - seccomp:unconfined #optional + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Amsterdam + - SUBFOLDER=/ #optional + - TITLE=Webtop #optional + - CUSTOM_USER=moni + - PASSWORD=Pd5oBZ3vN31wCkj8 + volumes: + - /home/moni/webtop/data:/config + - /var/run/docker.sock:/var/run/docker.sock #optional + ports: + - 3000:3000 + - 3001:3001 + devices: + - /dev/dri:/dev/dri #optional + shm_size: "1gb" #optional + restart: unless-stopped diff --git a/containers/docker/windows/compose.yaml b/containers/docker/windows/compose.yaml new file mode 100644 index 0000000..dea39a9 --- /dev/null +++ b/containers/docker/windows/compose.yaml @@ -0,0 +1,18 @@ +services: + windows: + image: dockurr/windows + container_name: windows + environment: + VERSION: "11" + devices: + - /dev/kvm + - /dev/net/tun + cap_add: + - NET_ADMIN + ports: + - 8006:8006 + - 3389:3389/tcp + - 3389:3389/udp + stop_grace_period: 2m + volumes: + - /var/win:/storage diff --git a/containers/docker/wordpress/docker-compose.yml b/containers/docker/wordpress/docker-compose.yml new file mode 100644 index 0000000..8254dc0 --- /dev/null +++ b/containers/docker/wordpress/docker-compose.yml @@ -0,0 +1,31 @@ +version: '3.1' + +services: + + wordpress: + image: wordpress + restart: always + ports: + - 8080:80 + environment: + WORDPRESS_DB_HOST: db + WORDPRESS_DB_USER: exampleuser + WORDPRESS_DB_PASSWORD: examplepass + WORDPRESS_DB_NAME: exampledb + volumes: + - wordpress:/var/www/html + + db: + image: mysql:8.0 + restart: always + environment: + MYSQL_DATABASE: exampledb + MYSQL_USER: exampleuser + MYSQL_PASSWORD: examplepass + MYSQL_RANDOM_ROOT_PASSWORD: '1' + volumes: + - db:/var/lib/mysql + +volumes: + wordpress: + db: \ No newline at end of file diff --git a/homeservers/Backup.md b/homeservers/Backup.md new file mode 100644 index 0000000..fdc93bd --- /dev/null +++ b/homeservers/Backup.md @@ -0,0 +1,86 @@ +# Syncthing + +```sh +sudo systemctl restart syncthing@photoprism.service +``` + +Admin interface: http://192.168.1.10:8384/ + +I have a user called `photoprism` and the password is in Bitwarden + +# Borg Backup to Hetzner Storage Box + +For backup I use [Borg](https://borgbackup.readthedocs.io/). I followed the steps described in this community article: + +* [Install and Configure BorgBackup](https://community.hetzner.com/tutorials/install-and-configure-borgbackup) + +We have two backups in the storage box: + +* `/./borgbackup/photoprism` +* `/./borgbackup/nextcloud` + +## Photoprism backup + +This is what I did for Photoprism + +The init script (which is only done once, be careful it will overwrite!): + +```sh +# (As root) +export BORG_RSH='ssh -i /root/.ssh/id_ed25519' +export BORG_PASSPHRASE="" +borg init --encryption=repokey ssh://u388089@u388089.your-storagebox.de:23/./borgbackup/photoprism +``` + +It outputs a key which I put in Bitwarden (you need it to decrypt the backup). + +To make a manual backup: + +```sh +export BORG_RSH='ssh -i /root/.ssh/id_ed25519' +export BORG_PASSPHRASE="" +borg create --stats ssh://u388089@u388089.your-storagebox.de:23/./borgbackup/photoprism::2024_11_24 /home/photoprism/Import/ /home/photoprism/Pictures/ +``` + +The above can be found in Bitwarden, look for "Hetzner Borg Backup Script for Photoprism" + +I also created a bash script in `/usr/local/bin/photoprism_backup.sh` (as mentioned in the article). + +You can find it here in this repo: + +* [photoprism_backup.sh](./scripts/photoprism_backup.sh) + +## Nextcloud backup + +Ok, this is confusing but Nextcloud uses Borgbackup internally. Go to the [aio interface](http://192.168.1.10:8080/) and you'll notice there are backups to: + +``` +/root/nextcloudbackup +``` + +So nextcloud keeps making backups to this directory. Not ideal (TODO: I gotta find a solution for this). + +But I can rclone the data: + +```sh +rclone sync -v /root/nextcloudbackup/ hetzner:/cloned_borgbackup/nextcloud +``` + +For this I also created a script and placed it in `/usr/local/bin` + +[nextcloud_backup.sh](./scripts/nextcloud_backup.sh) + +# Automatic backup + +* [cloud_backup.service](./scripts/cloud_backup.service) +* [cloud_backup.timer](./scripts/cloud_backup.timer) + +```sh +cp -v cloud_backup.service /etc/systemd/system/ +cp -v cloud_backup.timer /etc/systemd/system/ +``` + +```sh +systemctl enable cloud_backup.service +systemctl enable cloud_backup.timer +``` diff --git a/homeservers/Immich.md b/homeservers/Immich.md new file mode 100644 index 0000000..02b99ac --- /dev/null +++ b/homeservers/Immich.md @@ -0,0 +1,15 @@ +Compose file: + +[compose.yaml](../containers/docker/immich/compose.yaml) + +For immich I added a dedicated user: + +```sh +sudo useradd immich +``` + +This creates a `/home/immich` directory. We neet to run `cat /etc/passwd` to get the id of the user. + +Unfortunately, I have not been able to use the user id for the containers, so everything is written as root. I'm still using `/home/immich`, but all the files are written there as root. + +Eventually I might use podman but I don't know enough about it yet to get comfortable with it. diff --git a/homeservers/Jellyfin.md b/homeservers/Jellyfin.md new file mode 100644 index 0000000..4606095 --- /dev/null +++ b/homeservers/Jellyfin.md @@ -0,0 +1,21 @@ +# Jellyfin + +Compose file: + +* [compose.yaml](../containers/docker/jellyfin/compose.yaml) + +I made a user names `jellyfin` and put its uid:guid in the compose file. All the files are in the home directory of that user `/home/jellyfin`. + +The user also has the authorized_keys so I can do this: + +```sh +ssh jellyfin@192.168.1.10 +``` + +and proceed to add the directories for the media files: + +```sh +cd jellyfin/media/tvshows/ +mkdir "Show I Want To Watch (2020)" +mkdir "Season 01" +``` diff --git a/homeservers/Nextcloud.md b/homeservers/Nextcloud.md new file mode 100644 index 0000000..242ea7f --- /dev/null +++ b/homeservers/Nextcloud.md @@ -0,0 +1,22 @@ +# Nextcloud + +I have a dedicated user: + +```sh +sudo useradd nextcloud +``` + +I'm running the Nextcloud All-In-One container. In order to make it easy I have a [stop](../containers/docker/nextcloud/stop.sh) and [restart](../containers/docker/nextcloud/stop.sh) script. + +Nextcloud runs on + +* https://nextcloud.allisonandmoni.online/ + +When you first start it up, you need to first disable the SSL from within the container: + +* [Ugly hack README](../containers/docker/nextcloud/README.md) + +And then go to: + +* http://192.168.1.11:8080 + diff --git a/homeservers/PhotoPrism.md b/homeservers/PhotoPrism.md new file mode 100644 index 0000000..a5615bb --- /dev/null +++ b/homeservers/PhotoPrism.md @@ -0,0 +1,43 @@ +# Photoprism + +What we are really running on this machine is Photoprism using this [docker-compose](../containers/docker/photoprism/docker-compose.yml) file, like this: + +```sh +cd ~/projects/stuff/containers/docker/photoprism +docker compose up --detach +``` + +To stop it: + +```sh +docker compose down +``` + +To update it: + +```sh +docker compose pull +``` + +Photoprism is running on: https://photos.allisonandmoni.online/ + +I have set up a Systemd service and timer to automatically run the import once an hour. They can be found: + +* [photoprism_import.service](./scripts/photoprism_import.service) +* [photoprism_import_timer.service](./scripts/photoprism_import_timer.service) + +To install: + +```sh +cp -v photoprism_import.service /etc/systemd/system/ +cp -v photoprism_import.timer /etc/systemd/system/ +``` + +Enable: + +```sh +systemctl enable photoprism_import.service +systemctl enable photoprism_import.timer +``` + +In the `compose.yaml` file you'll notice I have the files stored in `/home/photoprism`. That's because I thought I could run it with podman as a dedicated user. I abandoned that idea and am now running it in Docker instead. The directories `/home/photoprism/Pictures` and `/home/photoprism/Import` are all owned by `root` diff --git a/homeservers/README.md b/homeservers/README.md new file mode 100644 index 0000000..d61417b --- /dev/null +++ b/homeservers/README.md @@ -0,0 +1,43 @@ +# General + +This file describes the setup of the servers I have running at home, with some instructions. + +At the moment I'm running: + +* Raspberry PI4 8GB: `ssh moni@192.168.1.108` +* Lenovo Thinkcentre M900, i5-6500T: `ssh moni@192.168.1.10`, 8GB RAM, 500G SSD +* Lenovo Thinkcentre M900, i5-6500T: `ssh moni@192.168.1.11`, 16GB RAM, 126G NVME + 1TB SSD + +For Raspberry PI, see [RPI](./RPI.md). That is where the routing happens. +For Lenovo Thinkcenter, see below. That is where all the action happens. + +# Lenovo Thinkcentre M900, i5-6500T + +I have two Lenovo Thinkcentres, one with 8GB of ram and 500GB. The other with 16GB of ram and an NVME of 128GB and an SSD of 1GB. + +The idea is to have one in production for all my servers running on `allisonandmoni.online` domain and the other is to play with. + +Right now my OS of choice is AlmaLinux, but that can change with my mood. + +On my production machine I am running Docker. + +You don't need to be root to use it: + +```sh +docker ps -a +``` + +Right now I'm running the following containers: + +* [PhotoPrism](./PhotoPrism.md), reachable by https://photos.allisonandmoni.online +* [JellyFin](./Jellyfin.md), reachable by https://jellyfin.allisonandmoni.online/ +* [Nextcloud](./Nextcloud.md), reachable by https://nextcloud.allisonandmoni.online/ +* [qBittorrent](./qBittorrent.md), reachable by https://qbittorrent.allisonandmoni.online/ +* [Immich](./Immich.md), reachable by https://photos2.allisonandmoni.online + +# Backup and restore + +I have backup and restore scripts and instructions. See here: + +* [Backup](./Backup.md) +* [Restore](./Restore.md) \ No newline at end of file diff --git a/homeservers/RPI.md b/homeservers/RPI.md new file mode 100644 index 0000000..91b2523 --- /dev/null +++ b/homeservers/RPI.md @@ -0,0 +1,53 @@ +# Raspberry PI + +On the Raspberry PI I am running FreeBSD with Bastille which currently has 1 jail: + +```sh +sudo bastille list +``` + +``` +ID IP Address Hostname Path +1 192.168.1.200 caddy /usr/local/bastille/jails/caddy/root +``` + +The ip address 192.168.1.200 is chosen deliberately. On my router (192.168.1.1) I have the following port mappings: + +* 32222 to 192.168.1.108:22 (ssh) - Maps to the Raspberry PI 8 for external access +* 80 to 192.168.1.200:80 (http) - Maps to the Caddy jail +* 443 to 192.168.1.200:443 (https) - Maps to the Caddy jail +* 3478 to 192.168.1.10 (Nextcloud talk) - This is a direct connection to the Lenovo machine. + +To view the Caddy setup in the Caddy jail, run this: + +```sh +sudo -i +bastille console caddy +cat /usr/local/etc/caddy/Caddyfile +``` + +Result: + +``` +www.allisonandmoni.online { + reverse_proxy 192.168.1.10:8081 +} + +photos.allisonandmoni.online { + reverse_proxy 192.168.1.10:2342 +} + +nextcloud.allisonandmoni.online { + reverse_proxy 192.168.1.10:11000 +} + +jellyfin.allisonandmoni.online { + reverse_proxy 192.168.1.10:8096 +} +``` + +As you can see I have setup reverse proxies for www, photos and nextcloud for allisonandmoni.online. + +The instructions for the caddy setup on my RPI4 can be found in this file: + +* [Caddy on FreeBSD](../os/FreeBSD/Bastille.md#caddy) diff --git a/homeservers/Restore.md b/homeservers/Restore.md new file mode 100644 index 0000000..c665cdb --- /dev/null +++ b/homeservers/Restore.md @@ -0,0 +1,62 @@ +# Restore + +## Borg backup + +First we need to install Borg Backup: + +```sh +dnf install -y borgbackup +``` + +## Hetzner Storage Box + +So I have a Hetzner Storage Box which is reachable by: + +```sh +ssh -p23 u388089@u388089.your-storagebox.de +``` + +There are two subdirectories: + +```sh +/borgbackup # This is an actual borg backup of PhotoPrism with history and everything +/cloned_borgbackup # This is a rclone of a borg backup of nextcloud that happens on my local machine. +``` + +First, we're going to talk about restoring PhotoPrism + +## Restore Photoprism + +List: + +```sh +export BORG_PASSPHRASE='' +borg list ssh://u388089@u388089.your-storagebox.de:23/./borgbackup/photoprism +``` + +(Optional, to see what you have) Mount a snapshot: + +```sh +borg mount ssh://u388089@u388089.your-storagebox.de:23/./borgbackup/photoprism::2025-01-14_04:00 /home/moni/borg_restore/ +``` + +Unmount it: + +```sh +borg umount /home/moni/borg_restore/ +``` + +Restore a backup + +You might want to tmux first: + +```sh +tmux +``` + +```sh +borg --progress --verbose extract ssh://u388089@u388089.your-storagebox.de:23/./borgbackup/photoprism::2025-01-14_04:00 /home/photoprism/Pictures +``` + +Giving a local target directory is not supported. The restore will be in a subdirectory. So if you're in `/home/moni` it will restore to `/home/moni/home/photoprism/Pictures`. You can't change this behavior. + diff --git a/homeservers/qBittorrent.md b/homeservers/qBittorrent.md new file mode 100644 index 0000000..a61d5c8 --- /dev/null +++ b/homeservers/qBittorrent.md @@ -0,0 +1,11 @@ +# qBittorrent + +Compose file: + +* [compose.yaml](../containers/docker/qbittorrent/docker-compose.yml) + +qBittorrent uses the same user as jellyfin which writes everything to `/home/jellyfin` + +I also put the `jellyfin` uid:guid into the qbitorrent container. The files are downloaded to the `bittorrent/downloads` subdirtory of `jellyfin` home. + +When I want to watch something I can copy it from the downloads directory to the media directory of jellyfin. diff --git a/homeservers/scripts/cloud_backup.service b/homeservers/scripts/cloud_backup.service new file mode 100644 index 0000000..674c6c1 --- /dev/null +++ b/homeservers/scripts/cloud_backup.service @@ -0,0 +1,8 @@ +[Unit] +Description=Run Cloud Backup + +[Service] +ExecStart=/bin/bash /usr/local/bin/cloud_backup.sh + +[Install] +WantedBy=multi-user.target diff --git a/homeservers/scripts/cloud_backup.sh b/homeservers/scripts/cloud_backup.sh new file mode 100644 index 0000000..a7544ed --- /dev/null +++ b/homeservers/scripts/cloud_backup.sh @@ -0,0 +1,3 @@ +#!/bin/bash +/bin/bash /usr/local/bin/photoprism_backup.sh +/bin/bash /usr/local/bin/nextcloud_backup.sh diff --git a/homeservers/scripts/cloud_backup.timer b/homeservers/scripts/cloud_backup.timer new file mode 100644 index 0000000..07bf5b3 --- /dev/null +++ b/homeservers/scripts/cloud_backup.timer @@ -0,0 +1,9 @@ +[Unit] +Description=Run PCloud Backup timer + +[Timer] +OnCalendar=0/4:00:00 +Unit=cloud_backup.service + +[Install] +WantedBy=timers.target diff --git a/homeservers/scripts/nextcloud_backup.sh b/homeservers/scripts/nextcloud_backup.sh new file mode 100644 index 0000000..6f3c01b --- /dev/null +++ b/homeservers/scripts/nextcloud_backup.sh @@ -0,0 +1 @@ +rclone sync /root/nextcloudbackup/ hetzner:/cloned_borgbackup/nextcloud --log-file /var/log/nextcloud_backup.log --log-level INFO \ No newline at end of file diff --git a/homeservers/scripts/photoprism_backup.sh b/homeservers/scripts/photoprism_backup.sh new file mode 100644 index 0000000..e282287 --- /dev/null +++ b/homeservers/scripts/photoprism_backup.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +## +## Set environment variables +## + +## if you don't use the standard SSH key, +## you have to specify the path to the key like this +export BORG_RSH='ssh -i /root/.ssh/id_ed25519' + +## You can save your borg passphrase in an environment +## variable, so you don't need to type it in when using borg +export BORG_PASSPHRASE='' + +## +## Set some variables +## + +LOG='/var/log/photoprism_backup.log' +export BACKUP_USER='u388089' +export REPOSITORY_DIR='photoprism' + +## Tip: If using with a Backup Space you have to use +## 'your-storagebox.de' instead of 'your-backup.de' + +export REPOSITORY="ssh://${BACKUP_USER}@${BACKUP_USER}.your-storagebox.de:23/./borgbackup/${REPOSITORY_DIR}" + +## +## Output to a logfile +## + +exec > >(tee -i ${LOG}) +exec 2>&1 + +echo "###### Backup started: $(date) ######" + +## +## At this place you could perform different tasks +## that will take place before the backup, e.g. +## +## - Create a list of installed software +## - Create a database dump +## + +## +## Transfer the files into the repository. +## In this example the folders root, etc, +## var/www and home will be saved. +## In addition you find a list of excludes that should not +## be in a backup and are excluded by default. +## + +echo "Transfer files ..." +borg create -v --stats \ + $REPOSITORY::'{now:%Y-%m-%d_%H:%M}' \ + /home/photoprism/Import/ \ + /home/photoprism/Pictures/ + +echo "###### Backup ended: $(date) ######" diff --git a/homeservers/scripts/photoprism_import.service b/homeservers/scripts/photoprism_import.service new file mode 100644 index 0000000..567702e --- /dev/null +++ b/homeservers/scripts/photoprism_import.service @@ -0,0 +1,8 @@ +[Unit] +Description=Run Photoprism Import + +[Service] +ExecStart=docker exec photoprism-photoprism-1 photoprism import + +[Install] +WantedBy=multi-user.target diff --git a/homeservers/scripts/photoprism_import.timer b/homeservers/scripts/photoprism_import.timer new file mode 100644 index 0000000..c5237c1 --- /dev/null +++ b/homeservers/scripts/photoprism_import.timer @@ -0,0 +1,9 @@ +[Unit] +Description=Run Photoprism Import script hourly + +[Timer] +OnCalendar=hourly +Unit=photoprism_import.service + +[Install] +WantedBy=timers.target diff --git a/laptops/Clevo_Laptop.md b/laptops/Clevo_Laptop.md new file mode 100644 index 0000000..f3a8e5f --- /dev/null +++ b/laptops/Clevo_Laptop.md @@ -0,0 +1,179 @@ +Clevo laptop which is sold by Tuxedo computers. + +# General + +Set the hostname: + +```sh +hostnamectl set-hostname moni-fedora # Fedora +hostnamectl set-hostname moni-opensuse # openSUSE +``` + +Install my favorite packages + +```sh +zypper install tmux htop neovim git ncdu podman # openSUSE +dnf install tmux htop neovim git ncdu podman # Fedora +``` + +# Suspend when laptop lid closed + +On openSUSE: + +On Fedora: + +```sh +nvim /usr/lib/systemd/logind.conf # Fedora & openSUSE +nvim /etc/systemd/logind.conf # Ubuntu +``` + +Uncomment the lines: + +```conf +HandleLidSwitch=suspend +HandleLidSwitchExternalPower=suspend +HandleLidSwitchDocked=ignore +LidSwitchIgnoreInhibited=yes +``` + +You need to reboot before it takes effect. + +# Wake on suspend + +There is a bug in Linux kernel 6. This article explains this: + +https://bugzilla.redhat.com/show_bug.cgi?id=2162013 + +Somehow the touchpad keeps waking up the laptop. + +```sh +cat /proc/acpi/wakeup +``` + +Should return: + +``` +Device S-state Status Sysfs node +GPP0 S0 *disabled +GPP1 S0 *disabled +GP17 S0 *enabled pci:0000:00:08.1 +``` + +## Temporarily disable + +To temporarily disable it do this: + +```sh +echo disabled > /sys/bus/i2c/devices/i2c-FTCS1000:00/power/wakeup +``` + +## Persistent settings + +As root: + +```sh +nvim /etc/systemd/system/disable-wakeup.service +``` + +Contents: + +``` +[Unit] +Description=Disable wakeup triggers + +[Service] +Type=oneshot +ExecStart=/bin/sh -c "echo disabled > /sys/bus/i2c/devices/i2c-FTCS1000\:00/power/wakeup ; echo GP17 > /proc/acpi/wakeup" +ExecStop=/bin/sh -c "echo disabled > /sys/bus/i2c/devices/i2c-FTCS1000\:00/power/wakeup ; echo GP17 > /proc/acpi/wakeup" +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +``` + +Then simply enable and start it: + +```sh +systemctl enable disable-wakeup.service +systemctl start disable-wakeup.service +``` + +Reboot the laptop and check to see if the systemd unit works: + +```sh +systemctl status disable-wakeup.service +``` + +```sh +cat /proc/acpi/wakeup +cat /sys/bus/i2c/devices/i2c-FTCS1000\:00/power/wakeup +``` + +Test it by suspending the laptop. You can also use: + +```sh +systemctl suspend -i +``` + +# Tuxedo control center + +Instructions: https://www.tuxedocomputers.com/en/Add-TUXEDO-software-package-sources.tuxedo + +## On openSUSE + +See instructions on page and then: + +```sh +zypper refresh && zypper install tuxedo-control-center +``` + + +## On Fedora + +```sh +nvim /etc/yum.repos.d/tuxedo.repo +``` + +Contents: + +``` +[tuxedo] +name=tuxedo +baseurl=https://rpm.tuxedocomputers.com/fedora/40/x86_64/base +enabled=1 +gpgcheck=1 +gpgkey=https://rpm.tuxedocomputers.com/fedora/40/0x54840598.pub.asc +skip_if_unavailable=False +``` + +Get the key: + +```sh +wget https://rpm.tuxedocomputers.com/fedora/40/0x54840598.pub.asc +``` + +And install it: + +```sh +rpm --import ./0x54840598.pub.asc +``` + +Now install the control center: + +```sh +dnf update +dnf install tuxedo-control-center +``` + +You need to reboot before it takes effect. + +# Virtualization + +```sh +dnf install @virtualization +``` + +```sh +systemctl enable libvirtd +systemctl start libvirtd +``` diff --git a/laptops/Ideapad_510.md b/laptops/Ideapad_510.md new file mode 100644 index 0000000..ab4c295 --- /dev/null +++ b/laptops/Ideapad_510.md @@ -0,0 +1,88 @@ +# Lenovo ideapad 510 + +## Prevent suspend when lid closed + +Add a new file: + +```sh +nvim /etc/systemd/logind.conf.d/no-suspend-on-lid.conf +``` + +Add this: + +``` +[Login] +HandleLidSwitch=ignore +HandleLidSwitchExternalPower=ignore +HandleLidSwitchDocked=ignore +``` + +```sh +systemctl restart systemd-logind +``` + +## Wake-on-lan + +See my network interfaces: + +```sh +ip link show +``` + +``` +1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 +2: enp1s0: mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 + link/ether 54:e1:ad:9d:a8:74 brd ff:ff:ff:ff:ff:ff +3: wlp2s0: mtu 1500 qdisc noqueue state DOWN mode DORMANT group default qlen 1000 + link/ether 52:37:c4:2c:79:38 brd ff:ff:ff:ff:ff:ff permaddr 3c:f8:62:b3:7f:81 +``` + +Enable for the ethernet + +```sh +ethtool -s enp1s0 wol g +``` + +Make it pemanent. Create a systemd unit: + +```sh +nvim /etc/systemd/system/wol.service +``` + +``` +[Unit] +Description=Wake-on-LAN +Requires=network.target +After=network.target + +[Service] +ExecStart=/usr/sbin/ethtool -s enp1s0 wol g +Type=oneshot + +[Install] +WantedBy=multi-user.target +``` + +```sh +systemctl enable wol.service +``` + +Check it: + +```sh +systemctl start wol.service +systemctl status wol.service +``` + +Suspend the laptop: + +```sh +systemctl suspend +``` + +Wake it up again from another machine: + +```sh +wakeonlan 54:e1:ad:9d:a8:74 +``` \ No newline at end of file diff --git a/laptops/Laptops.md b/laptops/Laptops.md new file mode 100644 index 0000000..b487837 --- /dev/null +++ b/laptops/Laptops.md @@ -0,0 +1,2 @@ +* [Clevo Laptop](Clevo_Laptop.md) +* [Lenovo ideapad 510](Ideapad_510.md) \ No newline at end of file diff --git a/os/AlmaLinux/AlmaLinux.md b/os/AlmaLinux/AlmaLinux.md new file mode 100644 index 0000000..181a631 --- /dev/null +++ b/os/AlmaLinux/AlmaLinux.md @@ -0,0 +1,90 @@ +# General + +Remove the web console and all the other stuff around it: + +```sh +dnf -y remove cockpit* +firewall-cmd --permanent --remove-service=cockpit +firewall-cmd --reload +firewall-cmd --list-all +``` + +Update + +```sh +dnf update +``` + +Set hostname + +```sh +hostnamectl set-hostname moni-alma +``` + +Set up EPEL, follow the instructions: + +* https://docs.fedoraproject.org/en-US/epel/getting-started/ + +And then install my favorites: + +```sh +dnf install -y tmux htop ncdu neovim git +``` + +Install docker: + +```sh +dnf -y install dnf-plugins-core +dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo +dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin +systemctl enable --now docker +``` + +Add the `moni` to the docker group: + +```sh +usermod -aG docker moni +``` + +You need to log out and log back in and then test it: + +```sh +# run without sudo +docker run -it --rm -p 8080:80 httpd:2.4 +``` + +It will run in the foreground because we didn't pass `-d`. + +Open your browser to: + +* http://192.168.1.11:8080 +* or http://192.168.1.10:8080 for the 8GB server + +You can stop the container with CTRL+C. It should clean itself up (`--rm`). + +Cleanup: + +```sh +docker image prune --all --force +``` + +# SSH + +```sh +nvim /etc/ssh/sshd_config +``` + +Uncomment/change these settings: + +```conf +PermitRootLogin no +# PubkeyAuthentication yes <-- This is the detault, so you don't need to change this +PasswordAuthentication no +KbdInteractiveAuthentication no +``` + +Reload: + +```sh +sudo systemctl reload sshd +``` diff --git a/os/FreeBSD/FreeBSD_on_Clevo_Laptop.md b/os/FreeBSD/FreeBSD_on_Clevo_Laptop.md new file mode 100644 index 0000000..d644db9 --- /dev/null +++ b/os/FreeBSD/FreeBSD_on_Clevo_Laptop.md @@ -0,0 +1,280 @@ +# Setup + +Install my favorite packages + +```sh +pkg install bash sudo tmux htop neovim git bastille +``` + +Add "wheel" to the suoers file: + +```sh +visudo +``` + +Change shell + +```sh +chsh -s /usr/local/bin/bash +``` + +Do a system update: + +```sh +freebsd-update fetch install +``` + +# Wifi + +Find out what network cards we have: + +```sh +pciconf -lv | grep -A1 -B3 network +``` + +On my Clevo laptop it looks like this: + +``` +re0@pci0:2:0:0: class=0x020000 rev=0x15 hdr=0x00 vendor=0x10ec device=0x8168 subvendor=0x1558 subdevice=0xa600 + vendor = 'Realtek Semiconductor Co., Ltd.' + device = 'RTL8111/8168/8211/8411 PCI Express Gigabit Ethernet Controller' + class = network + subclass = ethernet +iwlwifi0@pci0:3:0:0: class=0x028000 rev=0x1a hdr=0x00 vendor=0x8086 device=0x2723 subvendor=0x8086 subdevice=0x0084 + vendor = 'Intel Corporation' + device = 'Wi-Fi 6 AX200' + class = network +nvme0@pci0:4:0:0: class=0x010802 rev=0x00 hdr=0x00 vendor=0x144d device=0xa809 subvendor=0x144d subdevice=0xa801 +``` + +So we have an Intel Wifi. + +We're going to configure the wireless card iwlwifi0 to the interface wlan0: + +```sh +ifconfig wlan0 create wlandev iwlwifi0 +``` + +To make the change persist across reboots: + +```sh +sysrc wlans_iwlwifi0="wlan0" +``` + +We need to set the regulatory domain: + +```sh +ifconfig wlan0 regdomain ETSI country NL +``` + +To scan the networks. I had to run the command twice to see the list: + +```sh +ifconfig wlan0 up list scan +``` + +I see my networks: + +``` +SSID/MESH ID BSSID CHAN RATE S:N INT CAPS +TMNL-3EF981_24G d8:0d:17:b9:b2:f0 11 54M -37:-96 100 EPS HTCAP WME ATH RSN WPS +TMNL-3EF981 98:0d:67:3e:f9:81 11 54M -72:-96 100 EP APCHANREP RSN WPS BSSLOAD HTCAP VHTCAP VHTOPMODE WME +TMNL-3EF981_5G d8:0d:17:b9:b2:f1 64 54M -42:-96 100 EP HTCAP VHTCAP VHTOPMODE VHTPWRENV WME ATH RSN WPS +``` + +I want to connect to `TMNL-3EF981_5G`. We need to edit the `/etc/wpa_supplicant.conf` file: + +```sh +nvim /etc/wpa_supplicant.conf +``` + +The contents. The password need to be set in psk. + +``` +country=NL +network={ + ssid="TMNL-3EF981_5G" + psk="3J6YJHNRG8W7KMMF" + priority=5 +} +``` + +To set it to use DHCP: + +```sh +sysrc ifconfig_wlan0="WPA SYNCDHCP" +``` + +For some reason, we need to add the country code in the rc.conf: + +```sh +sysrc create_args_wlan0="country NL" +``` + +Now bring it up! + +```sh +service netif restart +``` + +Restart the laptop to see if it persists. + +For some reason, it won't work (no suprise, wifi is awful in FreeBSD). + +This worked for me after boot + +```sh +ifconfig wlan0 down +ifconfig wlan0 ssid "TMNL-3EF981_5G" +ifconfig wlan0 regdomain etsi2 country NL +service netif restart +``` + +# X11 + +https://docs.freebsd.org/en/books/handbook/x11/ + +Don't forget to start `tmux`: + +```sh +tmux +``` + +Add `moni` to the `video` group: + +```sh +pw groupmod video -m moni +``` + +And then install, but don't forget to read the messages when the install is complete! Scroll up with tmux + +```sh +pkg install xorg +``` + +This will improve mnuse and touchscreen support: + +```sh +sysctl kern.evdev.rcpt_mask=6 +``` + +And add this to `/etc/sysctl.conf` to persist it: + +```sh +kern.evdev.rcpt_mask=6 +``` + +# Amd + +```sh +pkg install drm-kmod +``` + +```sh +sysrc kld_list+=amdgpu +``` + +# Kde + +```sh +pkg install kde5 +``` + +```sh +sysrc dbus_enable="YES" +``` + +```sh +sysctl net.local.stream.recvspace=65536 +sysctl net.local.stream.sendspace=65536 +``` + +```sh +pkg install sddm +sysrc sddm_enable="YES" +``` + +# Fonts + +```sh +pkg install urwfonts +``` + +But you're not done yet, you need to add a conf file: + +```sh +nvim /usr/local/etc/X11/xorg.conf.d/90-fonts.conf +``` + +With the following: + +``` +Section "Files" + FontPath "/usr/local/share/fonts/urwfonts/" +EndSection +``` + +# CPU + +Too see your CPU 0 + +```sh +sysctl dev.cpu.0 +``` + +If you don't see a temperature: + +```sh +kldload amdtemp +``` + +Add it to startup: + +```sh +sysrc kld_list+=amdtemp +``` + +# Linux compatibility + +```sh +sysrc linux_enable="YES" +``` + +```sh +service linux start +``` + +# Final configs + +My `/etc/rc.conf` + +``` +hostname="moni-freebsd" +ifconfig_re0="DHCP" +sshd_enable="YES" +moused_enable="YES" +# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable +dumpdev="AUTO" +kld_list="amdgpu amdtemp" +dbus_enable="YES" +sddm_enable="YES" +linux_enable="YES" +create_args_wlan0="country NL" +wlans_iwlwifi0="wlan0" +ifconfig_wlan0="WPA SYNCDHCP" +``` + +My `/etc/sysctl.conf`: + +``` +# +# This file is read when going to multi-user and its contents piped thru +# ``sysctl'' to adjust kernel values. ``man 5 sysctl.conf'' for details. +# + +# Uncomment this to prevent users from seeing information about processes that +# are being run under another UID. +#security.bsd.see_other_uids=0 + +kern.evdev.rcpt_mask=6 +``` diff --git a/os/FreeBSD/FreeBSD_on_Lenovo_Thinkcentre.md b/os/FreeBSD/FreeBSD_on_Lenovo_Thinkcentre.md new file mode 100644 index 0000000..b63c33d --- /dev/null +++ b/os/FreeBSD/FreeBSD_on_Lenovo_Thinkcentre.md @@ -0,0 +1,111 @@ +# Setup + +First time: + +```sh +su - +``` + +Do a system update: + +```sh +freebsd-update fetch install +``` + +Update + +```sh +pkg update +``` + +Install my favorite packages + +```sh +pkg install -y bash sudo tmux htop neovim git ncdu bastille tailscale aria2 +``` + +Add "wheel" to the suoers file: + +```sh +visudo +``` + +Change shell + +```sh +chsh -s /usr/local/bin/bash +``` + +Tailscale + +```sh +service tailscaled enable +service tailscaled start +tailscale up +``` + +# SSH + +```sh +nvim /etc/ssh/sshd_config +``` + +Change this setting: + +``` +KbdInteractiveAuthentication no +``` + +That should be it. The config file should have these settings, including the commented lines shown as below: + +``` +#PermitRootLogin no +#PubkeyAuthentication yes +#PasswordAuthentication no +KbdInteractiveAuthentication no +#UsePAM yes +``` + +Test the setting + +```sh +sshd -t +``` + +Reload: + +```sh +service sshd reload +``` + +# PF + +Note, if you're using Bastille, the `/etc/pf.conf` file is going to look different. See [Bastille](../../containers/Bastille/Bastille.md). + +Now we need to get the filewall going. + +```sh +nvim /etc/pf.conf +``` + +Contents: + +``` +ext_if="em0" +block in all +pass in on $ext_if proto tcp to ($ext_if) port ssh +pass in on $ext_if proto tcp to ($ext_if) port 80 +pass in on $ext_if proto tcp to ($ext_if) port 443 +pass out all keep state +``` + +```sh +sysrc pf_enable=yes +service pf start +``` + +If you get the error 'no host key files found` then + +```sh +ssh-keygen -A +``` diff --git a/os/FreeBSD/FreeBSD_on_RPI.md b/os/FreeBSD/FreeBSD_on_RPI.md new file mode 100644 index 0000000..19f11b5 --- /dev/null +++ b/os/FreeBSD/FreeBSD_on_RPI.md @@ -0,0 +1,94 @@ +# General + +Default username passwords are: + +``` +freebsd +freebsd +``` + +and + +``` +root +root +``` + +```sh +ssh freebsd@192.168.1.108 +``` + +Setup ntpd + +You need to make sure that your date is close to the real time + +```sh +date 202406211441 +``` + +Set the timezone + +```sh +tzsetup +``` + +```sh +sysrc ntpd_enable=YES +``` + +Set the time: + +```sh +ntpdate -v -b in.pool.ntp.org +``` + +Start the service + +```sh +service ntpd start +``` + +Update the system + +```sh +pkg update +``` + +Install my favorite packages + +```sh +pkg install bash sudo tmux htop neovim git bastille +``` + +Add "wheel" to the suoers file: + +```sh +visudo +``` + +Change shell + +```sh +chsh -s /usr/local/bin/bash +``` + +Add a user + +```sh +adduser +``` + +Change the hostname + +```sh +sysrc hostname="rp4-8" +``` + +Delete the freebsd user that comes with the standard installation: + +```sh +rmuser freebsd +``` + +* [SSH instructions](FreeBSD_on_Lenovo_Thinkcentre.md#ssh) +* [PF instructions](FreeBSD_on_Lenovo_Thinkcentre.md#pf). Be careful, the network interface on the PI is called `genet0` not `em0`. diff --git a/os/FreeBSD/Storage.md b/os/FreeBSD/Storage.md new file mode 100644 index 0000000..d12fba1 --- /dev/null +++ b/os/FreeBSD/Storage.md @@ -0,0 +1,53 @@ +# Storage + +First we need to know what drives we have: + +```sh +geom disk list +``` + +Show the partitions: + +```sh +gpart show +``` + +In FreeBSD the partitions are named with suffix `pX`, for example `p1`, `p2`, etc. + +To mount the first partition of the external harddrive: + +```sh +# directory needs to exist +mkdir /mnt/usb + +# mount the first partition p1 +mount -t /dev/da0p1 /mnt/usb +``` + +# ZFS + +The handbook is actually quite comprehensive: + +* https://docs.freebsd.org/en/books/handbook/zfs/ + +In order for ZFS to work, we need to enable it. On the Raspberry PI it is not enabled by default. + +```sh +service zfs enable +service zfs start +``` + +You need empty space, either an empty partition or drive. + +# Mounting different filesystems (for example, a USB SDD) + +For ext4, see: https://docs.freebsd.org/en/books/handbook/filesystems/index.html#filesystems-linux + +For NTFS, see: https://docs.freebsd.org/en/books/handbook/disks/#using-ntfs + +Take into account that NTFS uses "Slices", not "Partitions" so mounting the external NTFS harddrive on the Raspberry PI looks like this: + +```sh +ntfs-3g /dev/da0s1 /mnt/usb +``` + diff --git a/ssh.md b/ssh.md new file mode 100644 index 0000000..b7aeb21 --- /dev/null +++ b/ssh.md @@ -0,0 +1,4 @@ +```sh +eval `ssh-agent -s` && ssh-add -k +ssh -t -A moni@143.179.250.91 -p 32222 ssh moni@192.168.1.10 +```