{"id":24253418,"url":"https://github.com/msz/blog-stack","last_synced_at":"2026-04-10T01:46:57.644Z","repository":{"id":80917519,"uuid":"112401121","full_name":"msz/blog-stack","owner":"msz","description":"🚢 Docker stack for convenient IPFS-based blog deployment","archived":false,"fork":false,"pushed_at":"2019-09-27T17:48:06.000Z","size":23,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-15T03:58:27.884Z","etag":null,"topics":["blog","docker","docker-compose","ipfs","static-site-generator"],"latest_commit_sha":null,"homepage":"http://michal.space","language":"JavaScript","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/msz.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}},"created_at":"2017-11-28T23:26:57.000Z","updated_at":"2020-09-08T14:07:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"0900a6fa-1951-4762-8d0c-1c2e27a66393","html_url":"https://github.com/msz/blog-stack","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msz%2Fblog-stack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msz%2Fblog-stack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msz%2Fblog-stack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msz%2Fblog-stack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/msz","download_url":"https://codeload.github.com/msz/blog-stack/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241898220,"owners_count":20039066,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["blog","docker","docker-compose","ipfs","static-site-generator"],"created_at":"2025-01-15T03:27:35.661Z","updated_at":"2025-12-04T04:04:29.355Z","avatar_url":"https://github.com/msz.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e As of 2019 I no longer use this. IPNS resolution took too much time for efficient operation of the site. However, if you only use IPFS names, you should be good!\n\n# my IPFS blog stack\n\nThis is the Docker service I use to deploy my Jekyll blog at\nhttp://michal.space. It does several things:\n\n* Watches blog source repository for new commits on GitHub and automatically\n  pulls them\n* Automatically builds each commit\n* Publishes the build directory under an IPNS name using the bundled IPFS node\n* Provides access to the IPFS node's web gateway with access limited to the\n  blog itself\n\nThe service's web gateway rewrites all URLs to refer to IPFS hashes and\nproxies the requests to the internal IPFS node. This way the website can be\neasily migrated to another technology because all existing links on the Web\ncan be IPFS-agnostic.\n\nThe service is designed to consist of easily swappable, self contained parts.\nFor example, it uses Jekyll but default, but it's as simple as swapping out\nthe `make-build` container to use another static site generator.\n\n## architecture\n\n![img](http://www.plantuml.com/plantuml/proxy?idx=0\u0026src=https://raw.github.com/msz/blog-stack/master/architecture.plantuml)\n\n## how to use\n\n1. Clone this repo\n2. Configure environment variables:\n    * `GIT_REPO` - the address of your blog source repository. If private, you\n      will need to add the service's SSH key as a deployment key, so use the\n      SSH URL for the repo.\n    * `GITHUB_TOKEN` - the GitHub webhook secret used to verify that incoming\n      webhook payloads really come from GitHub. A long random string works best.\n3.\n```\ndocker-compose up\n```\n4. [if your blog repo is private] Authorize the service's SSH key as a\n   deployment key so the service can pull your repository. You can find it in\n   the `ssh-data` Docker volume — it gets generated during the `make-pull`\n   container startup if it doesn't alredy exist. Add it as a deploy key using\n   [this guide](https://github.com/blog/2024-read-only-deploy-keys).\n5. Configure a GitHub webhook which will notify the service about new\n   commits. Go to `${your_repo}/settings/hooks/new` and choose the following:\n   * *Payload URL*: the URL which will point to the `make` container in the\n     service\n   * *Content type*: `application/json`\n   * *Secret*: the `GITHUB_TOKEN` you used in step 2\n   * Choose \"Just the `push` event\"\n6. Add a dnslink so your domain name will point to your IPNS hash. In your\n   domain's DNS, add a TXT entry with the following contents:\n   `dnslink=${IPNS_hash}`.\n7. Finally, push a new commit to your blog repo. The service should download\n   the repo, build the code, and publish to IPNS successfully.\n\n## why is it so overengineered?\n\nFor fun. I switched my VPS provider a few times in a short timespan and\nsetting up all components of the stack manually became quite tedious. I\nthought this was a good opportunity to learn how to use Docker. And it was!\n\nAn easy way to go about it would be to cram everything into a monolithic\nDocker container, but this goes against the Docker philosophy of one\ncontainer only doing one thing. It quickly became clear that the correct\nchoice would be a Docker service consisting of several interacting\ncontainers. It made sense to break everything down into atomic parts,\nincluding the build pipeline. For example, since I was recently looking into\nswitching from Jekyll to Hexo, I would want the ability to just swap out a\nJekyll container for a Hexo container and leave the rest of the setup the\nsame.\n\nFor the build pipeline, I ideally wanted containers that are run once in\nresponse to an event and then terminate. Sadly, Docker services only support\nlong-running application components. So I wrote\n[oneshot](https://github.com/msz/oneshot), a simple webhook server whose sole\npurpose is to run a shell command in response to a POST request. All build\npipeline components are oneshot servers which listen for events and each of\nthem takes care of running their own job in response to them. Refer to the\narchitecture diagram for details.\n\n## why ipfs\n\nI like the technology behind it and it's a pretty great solution for hosting\nand replicating static files. It's less great for publishing content that\nchanges (IPNS is slow), but it's okay for a blog.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmsz%2Fblog-stack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmsz%2Fblog-stack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmsz%2Fblog-stack/lists"}