{"id":16812687,"url":"https://github.com/joshua-anderson/rpi-selfhost-docker","last_synced_at":"2026-05-08T10:36:26.359Z","repository":{"id":73414344,"uuid":"252039045","full_name":"Joshua-Anderson/rpi-selfhost-docker","owner":"Joshua-Anderson","description":"Ansible scripts for selfhosted Raspberry Pi website cluster","archived":false,"fork":false,"pushed_at":"2021-01-31T03:51:21.000Z","size":43,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-11T15:00:26.690Z","etag":null,"topics":["ansible","docker","raspberry-pi"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Joshua-Anderson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-04-01T01:18:53.000Z","updated_at":"2024-02-24T16:01:47.000Z","dependencies_parsed_at":"2023-02-28T00:30:38.174Z","dependency_job_id":null,"html_url":"https://github.com/Joshua-Anderson/rpi-selfhost-docker","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Joshua-Anderson/rpi-selfhost-docker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Joshua-Anderson%2Frpi-selfhost-docker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Joshua-Anderson%2Frpi-selfhost-docker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Joshua-Anderson%2Frpi-selfhost-docker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Joshua-Anderson%2Frpi-selfhost-docker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Joshua-Anderson","download_url":"https://codeload.github.com/Joshua-Anderson/rpi-selfhost-docker/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Joshua-Anderson%2Frpi-selfhost-docker/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32776952,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"ssl_error","status_checked_at":"2026-05-08T08:22:45.650Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ansible","docker","raspberry-pi"],"created_at":"2024-10-13T10:23:01.844Z","updated_at":"2026-05-08T10:36:26.345Z","avatar_url":"https://github.com/Joshua-Anderson.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Raspberry Pi Containerized Selfhosting Cluster\n\nThis repository contains the ansible playbooks I used to self host my personal websites,\ncurrently miniflux (a minimalist rss reader) and gitea (a git source forge) on my raspberry pi cluster.\n\nAdditional Features:\n- Everything is dockerized! This isolates all the services from one another and makes future updates easy.\n- Automated backups using duplicty\n- Cluster wide log and telemetry monitoring using telegraf, influxdb, and chronograf.\n- Automatic letsencrypt ssl encryption using a caddy reverse proxy.\n\nI'll be adding to and improving this over time as I add more services to my cluster.\n\nI built this specifically for my use case with three raspberry pis running specific websites.\nSince you're probably not interested in running the same websites as me, this is meant mostly as a starting\npoint for how to self-host containerized websites on a raspberry pi with docker.\n\n## Installation\n\nThis ansible playbook is specifically tailored to run with three raspberry pis imaged with the\nraspbain buster minimal edition. Since I made some raspbian specific tweaks, running this script\non anything other that raspbain buster will cause some minor errors.\n\nBefore you can run the ansible playbook, static IPv4 ethernet addresses should be assigned to each of the pis,\nideally using your router with dhcp.\n\nI also highly recommend assigning easily readable dns records for those ip addresses, so that if\nyou want to change ip addresses later all you have to do is update the dns record, rather than\nre-run the ansible playbook to re-configure the pis.\n\nYou should also change the default password on the raspberry pis and setup ssh keys so ansible can\naccess the pis.\n\n1. Update the urls in the `example_hosts` file to point to your three raspberry pis\n2. Set the passwords and variables in the `example_vars.yml` file. Caddy will automatically use\n   letsencrypt to create ssl certificates, so make sure that your gitea and miniflux dns entries are\n   good before running the playbook!\n3. Run `ansible-playbook -i example_hosts ansible-setup.yml` to configure the raspberry pis.\n   Ansible script are mostly immutable, so they can be safely re-run if any errors come up in the progress.\n4. Setup the first miniflux user by sshing into the frontend pi and running `sudo docker exec -it miniflux /usr/bin/miniflux -create-admin`\n5. Setup the first gittea user by visiting the `https://\u003cgitea url\u003e/install` page.\n6. To make sure everything is working, check the metrics using chronograf at `\u003cbackend url\u003e:8888`.\n\n## Architecture\n\nI'm using three raspberry pi 3b+ computers for my cluster running raspbian.\n\n- frontend pi: The frontend pi hosts the frontend layers of my cluster, specifically miniflux and gitea, as well and the\n  caddy reverse proxy used to access them.\n- backend pi: The backend pi is used to host the data storage layers of my cluster. It has a postgres db used by\n  miniflux and gitea, as well as influxdb and chronograf for monitoring.\n- builder pi: The builder pi is used for testing services and building the docker containers used in the cluster.\n  It also also functions as a backup destination for the other two raspberry pis.\n\nSince every processes runs within docker, I mount in directories from the host to persist data across reboots.\nEverything in the `/usr/local/cfg` directory is read only configuration mounted into docker containers.\nEverything in the `/usr/local/persist` directory is persistent data mounted from docker containers.\n\n## Networking\n\nThe three raspberry pis need unrestricted communication with each other.\nRight now the postgres db connection and the chronograf web ui are unencrypted,\nso it's recommended that the pis are on their own private subnet.\n\nThe frontend raspberry pi hosts the frontend webpages, so it needs to have port 80 and 443\nexposed to the public internet. For IPv4 this means enabling port forwarding for those\nports on your router. For IPv6 you will probably need to open up incoming traffic on those ports\nto the frontend pi on your router's firewall.\n\nBecause raspbian defaults to using temporary IPv6 address, the ansible playbook switches dhcpd over\nto using permanent hardware based IPv6 addresses.\n\nWifi and bluetooth are also disabled by the ansible playbook to reduce power consumption\n\n## SSL\n\nCaddy has support for automatically configuring letsencrypt ssl certificates on startup.\nI have caddy act as a reverse proxy, where it listens for encrypted traffic,\nthen forwards the unencrypted traffic on a private docker network to the actual website.\n\nLet's Encrypt uses http requests to verify ownership of a domain. This means that if anything networking is\nmisconfigured in the cluster, caddy can get stuck in a loop requesting certificates from letsencrypt and get rate\nlimited for hours/days. I recommend testing with letsencrypt staging first to make sure everything is\nconfigured properly. The staging server doesn't provide browser truster certificates, but it has a higher\nrate limit, so you can test the certificate request process is working properly.\nTo use the staging server, uncomment the staging line in the caddy configuration template in the files directory.\n\n## Backups\n\nThe frontend and backend pis are setup to backup the `/usr/local/persist` directory over rsync/ssh with duplicity.\nThe ansible script creates a `bkup` user on the builder pi, then generates ssh keys on the frontend\nand backend, then adds the frontend and backend keys as authorized keys for the backup user.\nDuplicity will make incremental backups every other day and create a new full backup once a week.\n\nI opted not to go with encrypted backups because the `/usr/local/persist` directory is unencrypted\non the frontend and the backend. This means that encryption would only provide benefits if somebody\nhacked into the builder pi but not the other two, which I consider to be a low risk.\n\nAdditionally, I have script that runs weekly and replicates the backups on the builder pi to a\ngoogle cloud storage bucket. These backups are encrypted with the supplied pgp key, so they should\nbe unrecoverable to anybody except the user. Note that duplicity's backup replication functionality\nrequires duplicity \u003e 0.8 and will not work out of the box with raspbian. I wound up manually\ninstalling duplicity on this machine until a new enough version of duplicity lands in raspbian,\nwhich should be within the next six months.\n\n## Monitoring\n\nEach pi runs a telegraf container for telemetry acquisition.\nTelegraf sends it's telemetry to an influxdb database on the backend pi.\nTo visualize the telemetry, a read-only chronograf instance on the backend pi connects to influxdb\nto display cluster wide metrics and logs.\n\nTo send the docker container and host logs to influxdb, I configured docker to log to journald, then rsyslog to forward the host's journald logs to telegraf.\n\n## Software Updates\n\nThe ansible playbook is setup to automatically install updates nightly on all of the pis.\nIt is not setup automatically reboot after kernel upgrades, so I recommend checking every week\nto see if the pis need to be rebooted.\n\nAny of the containers can be upgraded by changing the image version in the ansible playbook and re-running it.\n\n## Why not Kubernetes?\n\nDespite the fact that I'm hosting a cluster of docker containers, I'm not using kubernetes.\nKubernetes is awesome, but it has a ton a complexity, overhead, and moving parts.\n\nWhen you're running outside of a cloud environment, you also lose a lot of kubernetes's benefits,\nlike being able to transfer containers and their associated data between nodes, or automatically\ncreating a load balancer that routes to your container.\n\nTo me, in this situation, the simplicity and reliability I get from running raw docker outweighs the\nmanagement benefits of kubernetes.\n\n## Todo\n\n- Setup minio/caddy for serving static files.\n- Setup a coredns server to act as a dns-over-tls proxy for the network.\n- tls encrypted postgres connections\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoshua-anderson%2Frpi-selfhost-docker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoshua-anderson%2Frpi-selfhost-docker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoshua-anderson%2Frpi-selfhost-docker/lists"}