A self hosted cloud
- Install Ansible locally via:
brew install ansible ansible-lint
pacman -S ansible
- Install Ansible packages via:
ansible-galaxy install willshersystems.sshd
ansible-playbook -i inventory.yaml playbook.yaml
- SSH into nodes and manage individual applications directly
For each new machine:
- Install Ubuntu LTS
- Username:
cloud
- Enable SSH server
- Import SSH keys from GitHub
- Username:
- Bootstrap as root using
bash <(curl https://raw.githubusercontent.com/noizwaves/noizwaves.cloud/main/ansible/bootstrap.sh)
- Connect to Tailscale
- Run anisble playbook
- Populate
.envrc
- Setup crontabs
- Add to the inventory and ssh config
$ crontab -e
- Enable automated daily 1am hot backups with this cron config:
SHELL=/bin/bash
MAILTO=""
HOME=/home/cloud
0 1 * * * ./cloud-config/backups/hot/backup.sh >cloud-config/hot_backup.log 2>&1
- Enable DNS healthchecks with cron config:
*/2 * * * * host www.google.com 127.0.0.1 && <healthchecks.io health check>
See here
$ cd traefik
$ mkdir -p ~/cloud-data/traefik/letsencrypt
$ cp .env.tmpl .env
- Input appropriate values
$ docker-compose up -d
- Open Traefik dashboard at
https://traefik.odroid.noizwaves.cloud
$ cd watchtower
$ cp .env.tmpl .env
- Input appropriate values
$ docker-compose up -d
$ cd pihole
$ mkdir -p ~/cloud-data/pihole/config ~/cloud-data/pihole/dnsmasq
$ cp .env.tmpl .env
- Input appropriate values
$ docker-compose up -d
$ cd authelia
$ mkdir -p ~/cloud-data/authelia/mariadb ~/cloud-data/authelia/redis
- Create a user database
$ cp config/users_database.yml.tmpl config/users_database.yml
- Follow steps in comments to create users
- Create configuration
$ cp config/configuration.yml.tmpl config/configuration.yml
- Replace all
${CLOUD_DOMAIN}
with desired value
- Create secrets
$ mkdir -p .secrets
$ openssl rand -base64 32 > .secrets/jwt.txt
$ openssl rand -base64 32 > .secrets/session.txt
$ openssl rand -base64 32 > .secrets/mysql_password.txt
$ cp .env.tmpl .env
- Fill in appropriate values
$ docker-compose up -d
$ cd minio
$ mkdir -p ~/cloud-data/minio/data
$ cp .env.tmpl .env
- Fill in appropriate values
$ docker-compose up -d
- Navigate to Minio Console
- Use S3-compatible API using:
- Bucket Name:
s3://BUCKET_NAME
- Endpoint URL:
https://s3.dell.noizwaves.cloud
- Region Name:
dell
- Bucket Name:
$ cd influxdb
$ mkdir -p ~/cloud-data/influxdb/data
$ docker-compose up -d
- Visit influxdb and set up data collection
$ cd bitwarden
$ mkdir -p ~/cloud-data/bitwarden/data
$ cp .env.tmpl .env
- Input appropriate values
- Change value of
SIGNUPS_ALLOWED
to'true'
$ docker-compose up -d
- Create user
- Change value of
SIGNUPS_ALLOWED
to'false'
$ docker-compose up -d
$ cd speedtest
$ docker-compose up -d
- Visit Speedtest
$ cd upsnap
$ mkdir -p ~/cloud-data/upsnap/data
$ docker-compose up -d
- Visit UpSnap
$ cd seafile
$ cp .env.tmpl .env
- Input appropriate values
$ docker-compose up -d
- Log in, navigate to
System Admin > Settings
and updateSERVICE_URL
&FILE_SERVER_ROOT
- Edit config files at
SEAFILE_DATA=$(docker volume inspect seafile_data --format '{{ .Mountpoint }}')
- Edit the value of
FILE_SERVER_ROOT
in$SEAFILE_DATA/seafile/conf/seahub_settings.py
- Edit the value of
enabled
in$SEAFILE_DATA/seafile/conf/seafdav.conf
- Edit
$SEAFILE_DATA/seafile/conf/ccnet.conf
- Edit the value of
- Restart memcached
$ docker-compose restart memcached
$ cd nextcloud
$ mkdir -p ~/cloud-data/nextcloud/data ~/cloud-data/nextcloud/config ~/cloud-data/nextcloud/mariadb
$ cp .env.tmpl .env
- Input appropriate values
$ docker-compose up -d
- Open Nextcloud
- Configure application to use MySQL with the following settings:
- Database name:
nextcloud
- Username:
nextcloud
- Password: value from
.env
- Host:
mariadb
- Database name:
- Edit
/config/www/nextcloud/config/config.php
- Add
trusted_proxies
array that includesweb
network CIDR ($ docker network inspect web
)
- Add
$ cd resilio-sync
$ mkdir -p ~/cloud-data/resilio-sync/data ~/cloud-data/resilio-sync/config
$ docker-compose up -d
- Open Resilio Sync
- Configure application
$ cd freshrss
$ mkdir -p ~/cloud-data/freshrss/config ~/cloud-data/freshrss/mariadb
$ cp .env.tmpl .env
- Input appropriate values
$ docker-compose up -d
- Open FreshRSS
- Configure application
- Database type:
MySQL
- Host:
mariadb
- Database username:
freshrss
- Database password:
<value from .env file>
- Database:
freshrss
- Table prefix: `` (empty string)
- Database type:
$ cd ~/cloud-config/standardnotes
$ mkdir -p ~/cloud-data/standardnotes/mariadb
$ cp .env.tmpl .env
- Input appropriate values
$ docker-compose up -d
- Open Standard Notes web
- Create account
- Install extensions (via
Extensions > Import Extension > url > Enter > Install
):
-
$ cd fotos
-
$ mkdir -p ~/cloud-data/fotos/thumbnails/v2 ~/cloud-data/fotos/normals
-
Create WebDAV credentials via
$ htpasswd -c credentials.list <username>
and then enter the password. -
$ docker-compose up -d
-
$ cd .../fotos-lauren
-
$ mkdir -p ~/cloud-data/fotos-lauren/thumbnails/v2 ~/cloud-data/fotos-lauren/normals
-
Create WebDAV credentials via
$ htpasswd -c credentials.list <username>
and then enter the password. -
$ docker-compose up -d
$ cd firefly-iii
$ mkdir -p ~/cloud-data/firefly-iii/export ~/cloud-data/firefly-iii/upload ~/cloud-data/firefly-iii/mariadb
$ cp .env.tmpl .env
- Input appropriate values
$ docker-compose up -d
- Open Firefly III
$ cd photostructure
$ mkdir -p ~/cloud-data/photostructure/library ~/cloud-data/photostructure/tmp ~/cloud-data/photostructure/config ~/cloud-data/photostructure/logs
$ docker-compose up -d
- Open Photostructure
$ cd photoprism
$ mkdir -p ~/cloud-data/photoprism/storage
$ docker-compose up -d
- Open Photoprism
$ cd tandoor
$ mkdir -p ~/cloud-data/tandoor/postgres ~/cloud-data/tandoor/staticfiles ~/cloud-data/tandoor/mediafiles
$ cp .env.tmpl .env
- Input appropriate values
$ docker-compose up -d
- Open Tandoor
Plex (inspired by pierre-emmanuelJ/plex-traefik)
$ cd plex
$ mkdir -p ~/cloud-data/plex/config ~/cloud-data/plex/data ~/cloud-data/plex/transcode
- Generate a claim
$ docker-compose up -d
- Open Plex
This moves AV1 transcoding from the server onto the AppleTV.
$ curl -Ls -o ~/cloud-data/plex/config/Library/Application\ Support/Plex\ Media\ Server/Profiles/tvOS.xml https://raw.githubusercontent.com/currifi/plex_av1_tvos/main/tvOS.xml
$ docker-compose restart
$ cd registry
$ mkdir -p ~/cloud-data/registry/data
$ docker compose up -d
$ docker login registry.noizwaves.cloud
$ cd focalboard
$ mkdir ~/cloud-data/focalboard/files
$ touch ~/cloud-data/focalboard/focalboard.db
$ cp config.json.tmpl ~/cloud-data/focalboard/config.json
- Input appropriate values
$ docker-compose up -d
- Open Focalboard
$ cd filebrowser
$ mkdir ~/cloud-data/filebrowser
$ touch ~/cloud-data/filebrowser/database.db
$ cp filebrowser.json.tmpl ~/cloud-data/filebrowser/filebrowser.json
- Input appropriate values
- Initialize configuration by running
$ docker run --rm \ -v /home/cloud/cloud-data/filebrowser/filebrowser.json:/.filebrowser.json \ -v /home/cloud/cloud-data/filebrowser/database.db:/database.db \ filebrowser/filebrowser \ config init
- Switch to Proxy Header based authentication method by running
$ docker run --rm \ -v /home/cloud/cloud-data/filebrowser/filebrowser.json:/.filebrowser.json \ -v /home/cloud/cloud-data/filebrowser/database.db:/database.db \ filebrowser/filebrowser \ config set --auth.method=proxy --auth.header=Remote-User
- Create admin users by running
$ docker run --rm \ -v /home/cloud/cloud-data/filebrowser/filebrowser.json:/.filebrowser.json \ -v /home/cloud/cloud-data/filebrowser/database.db:/database.db \ filebrowser/filebrowser \ users add $USERNAME $PASSWORD --perm.admin=true --perm.execute=false
$ docker-compose up -d
- Open Filebrowser
For private network DNS resolution
$ cd adguard
$ mkdir -p ~/cloud-data/adguard/work ~/cloud-data/adguard/conf ~/cloud-data/adguard/tailscale
$ docker-compose up -d
- Open Adguard Home and set it up
$ cd running
$ docker-compose up -d
- Open Running
$ cd vikunja
$ mkdir -p ~/cloud-data/vikunja/files ~/cloud-data/vikunja/mariadb
$ cp .env.tmpl .env
- Input appropriate values
- Clone source code
$ mkdir ~/workspace
$ git clone https://kolaente.dev/vikunja/api.git ~/workspace/vikunja-api
$ git clone https://kolaente.dev/vikunja/frontend.git ~/workspace/vikunja-frontend
$ docker-compose up -d
- Open Vikunja
$ cd syncthing
$ mkdir -p ~/cloud-data/syncthing/config
$ docker-compose up -d
- Open Syncthing
- Open Vikunja
$ cd gitea
$ mkdir -p ~/cloud-data/gitea/data
$ docker-compose up -d
- Open Gitea and complete initialization
- Update configuration
webhook.ALLOWED_HOST_LIST
to*.noizwaves.cloud
$ cd drone
$ mkdir -p ~/cloud-data/drone/data
$ cp .env.tmpl .env
- Update values in
.env
$ docker-compose up -d
- Open Drone and complete installation
$ cd trilio
$ mkdir -p ~/cloud-data/trilio
$ docker-compose up -d
- Open Trilio
$ cd matrix
$ mkdir -p ~/cloud-data/matrix/data ~/cloud-data/matrix/postgres ~/cloud-data/matrix/telegram
$ cp .env.tmpl .env
- Input appropriate values
$ docker-compose run --rm -e SYNAPSE_SERVER_NAME=matrix.noizwaves.cloud -e SYNAPSE_REPORT_STATS=no synapse generate
- Edit Synapse config using
$ vim ~/cloud-data/matrix/data/homeserver.yaml
- Generate Telegram config using
$ docker-compose run --rm telegram
- Edit Telegram config using
$ vim ~/cloud-data/matrix/telegram/config.yaml
1 Generate Telegram appservice registration using$ docker-compose run --rm telegram
$ docker-compose up -d
- Register users by running
$ docker-compose exec synapse register_new_matrix_user -c /data/homeserver.yaml http://localhost:8008
- Open Synapse
Based upon the instructions.
Prepare synapse on server:
$ cd matrix
$ mkdir -p ~/cloud-data/matrix/imessage plugins
$ wget -o plugins/shared_secret_authenticator.py https://raw.githubusercontent.com/devture/matrix-synapse-shared-secret-auth/master/shared_secret_authenticator.py
- Edit
~/cloud-data/matrix/data/homeserver.yaml
and add a new item topassword_providers
:Where:- module: "shared_secret_authenticator.SharedSecretAuthenticator" config: sharedSecret: "${SHARED_SECRET_AUTH_SECRET}"
SHARED_SECRET_AUTH_SECRET
=$ pwgen -s 128 1
Prepare bridge on mac:
- Identify mac to use to run bridge and setup iCloud (Messages and Contacts)
- Download latest release of mautrix-imessage to mac
- Extract to a folder
$ cp example-config.yaml config.yaml
and edit values:homeserver.address
tohttps://matrix.noizwaves.cloud
homeserver.websocket_proxy
towss://matrix-wsproxy.noizwaves.cloud
homeserver.domain
tonoizwaves.cloud
bridge.user
to@adam:noizwaves.cloud
bridge.login_shared_secret
to${SHARED_SECRET_AUTH_SECRET}
$ ./mautrix-imessage -g
- Ensure that
config.yaml
containsappservice.as_token
andappservice.hs_token
fromregistration.yaml
- Copy
registration.yaml
from mac to~/cloud-data/matrix/imessage/registration.yaml
on server
Prepare wsproxy on server:
cp .env_wsproxy.tmpl .env_wsproxy
- Input appropriate values (from
~/cloud-data/matrix/imessage/registration.yaml
) - Ensure that the
~/cloud-data/matrix/imessage:/bridges/imessage
volume mount is present forsynapse
$ docker-compose up -d synapse wsproxy
Start iMessage bridge on mac:
$ ./mautrix-imessage
(and if required, grant permission to read Contacts)
- Create instance
ufw
enable 22- Configure NGINX with
noizwaves-cloud-private-proxy.conf
:stream { upstream web_server { server 192.168.196.57:443; } server { listen 8443; proxy_pass web_server; } }
$ cd proxy_client
$ cp .env.tmpl .env
- Input appropriate values for the server
$ docker network create noizwaves_cloud_proxy
$ docker-compose up -d
- Find proxy container's IP address (
$IP_ADDRESS
) in$ docker-compose logs -f
- Add entry to
/etc/hosts
that resolve to$IP_ADDRESS
, ie:$IP_ADDRESS bitwarden.noizwaves.cloud nextcloud.noizwaves.cloud seafile.noizwaves.cloud authelia.noizwaves.cloud traefik.noizwaves.cloud
- Update tags to desired newer value
- Recreate containers via
$ docker-compose up --force-recreate --build -d
$ cd cloud-config
$ cp backups/backup.env.tmpl backups/backup.env
- Set appropriate values
$ cd cloud-config
$ cp backups/hot/hot.env.tmpl backups/hot/hot.env
- Set appropriate values
- Install crontab as mentioned above
$ cd ~/cloud-config
- Edit
backups/hot/restore.sh
to specify path to restore - Restore backup by
$ ./backups/hot/restore.sh
$ git restore backups/hot/restore.sh
- Set up restore device
- Follow steps for adding new node
- Install Docker using
sudo apt-get install docker.io
$ git clone https://github.com/noizwaves/noizwaves.cloud.git ~/cloud-config-recovery
$ cd ~/cloud-config-recovery
- Obtain secrets pack
- Populate config
$ cp backups/hot/hot.env.tmpl backups/hot/hot.env
- Set secrets and
RESTORE_DIR
$ .backups/hot/restore.sh
$ mv ~/recovery/cloud-config ~/
$ mv ~/recovery/cloud-data ~/
- Update configuration in
~/cloud-config/.envrc
- Manually add DNS entry for Adguard to Cloudflare
- Start foundational services (
traefik
,authelia
,adguard
) - Manually add DNS entries to Adguard
- Start applications, adding DNS entries to Adguard
$ cd cloud-config
$ cp backups/cold/cold.env.tmpl backups/cold/cold.env
- Set appropriate values
- SSH into
noizwaves.cloud
- Connect cold backup USB drive to host
- Mount backup drive via
$ pmount /dev/sda backup
- Mount bigbackup drive via
$ pmount /dev/sdb bigbackup
- Run a backup via
$ ~/cloud-config/backups/cold/backup.sh
- Unmount drives via
$ pumount backup
and$ pumount bigbackup
$ cd ~/cloud-config
- Edit
backups/cold/restore.sh
to specify path to restore - Connect cold backup drive
- Mount drive via
$ pmount /dev/sda backup
- Restore backup by
$ ./backups/cold/restore.sh
- Unmount drive via
$ pumount backup
$ git restore backups/cold/restore.sh
- Set up restore device
- Follow steps for adding new node
- Install Docker using
sudo apt-get install docker.io
$ git clone https://github.com/noizwaves/noizwaves.cloud.git ~/cloud-config-recovery
$ cd ~/cloud-config-recovery
- Obtain secrets pack
- Connect cold backup USB drive to restore target
- Obtain secrets pack
$ pmount /dev/sda backup
$ cp backups/cold/cold.env.tmpl backups/cold/cold.env
- Set
RESTORE_DIR
$ ./backups/cold/restore.sh
$ pumount backup
- Disconnect drive
$ mv ~/recovery/cloud-config ~/
$ mv ~/recovery/cloud-data ~/
- Update configuration in
~/cloud-config/.envrc
- Manually add DNS entry for Adguard to Cloudflare
- Start foundational services (
traefik
,authelia
,adguard
) - Manually add DNS entries to Adguard
- Start applications, adding DNS entries to Adguard
How to recover from total hardware failure/destruction
- Obtain secrets pack from secure storage
- Prepare restore target device
- Perform either a cold or hot restore
sudo apt install iotop
iotop
sudo apt install iozone3
sudo iozone -e -I -a -s 100M -r 4k -r 16k -r 512k -r 1024k -r 16384k -i 0 -i 1 -i 2
sudo apt install hdparm
sudo hdparm -t /dev/nvme0n1
sudo apt install inxi
inxi -Dxxx