{"id":30450322,"url":"https://github.com/mathiaspius/alpine-on-hetzner","last_synced_at":"2025-08-23T13:27:06.034Z","repository":{"id":122464614,"uuid":"453629863","full_name":"MathiasPius/alpine-on-hetzner","owner":"MathiasPius","description":"Tool for building cloud-init ready Alpine snapshots on Hetzner Cloud","archived":false,"fork":false,"pushed_at":"2024-06-08T14:27:44.000Z","size":48,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-06-08T15:38:16.169Z","etag":null,"topics":["alpine-linux","ansible","hetzner","hetzner-cloud","packer"],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MathiasPius.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2022-01-30T08:48:33.000Z","updated_at":"2024-06-08T15:38:16.382Z","dependencies_parsed_at":"2024-06-08T15:38:10.360Z","dependency_job_id":null,"html_url":"https://github.com/MathiasPius/alpine-on-hetzner","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/MathiasPius/alpine-on-hetzner","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MathiasPius%2Falpine-on-hetzner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MathiasPius%2Falpine-on-hetzner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MathiasPius%2Falpine-on-hetzner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MathiasPius%2Falpine-on-hetzner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MathiasPius","download_url":"https://codeload.github.com/MathiasPius/alpine-on-hetzner/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MathiasPius%2Falpine-on-hetzner/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271749048,"owners_count":24814115,"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","status":"online","status_checked_at":"2025-08-23T02:00:09.327Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["alpine-linux","ansible","hetzner","hetzner-cloud","packer"],"created_at":"2025-08-23T13:27:03.720Z","updated_at":"2025-08-23T13:27:06.020Z","avatar_url":"https://github.com/MathiasPius.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# alpine-hetzner\nTool for building cloud-init ready Alpine snapshots on Hetzner Cloud.\n\nYou can either run it as a docker container or as a regular packer build (see [entrypoint.sh](/entrypoint.sh) for hints on how), but this latter method is not officially supported.\n\n# Examples\n\n### Create an alpine image with the [default](/default.json) configuration\nRunning this will create an `alpine` snapshot within your Hetzner Cloud project, ready to use for creating new servers. See the [launching a server](#launching-a-server) section for how to test it!\n```shell\ndocker run -it --rm -e \"HCLOUD_TOKEN=\u003cYourTokenHere\u003e\" ghcr.io/mathiaspius/alpine-on-hetzner:latest\n```\n\n### Default image, with `doas` installed, and `template.local` as default hostname\nConfiguration values can be overwritten by creating new configuration file with just the changes you want, and supplying the path as an argument when running it. See [Custom Configuration](#custom-configuration) for technical details on how the values are merged.\n```shell\nmkdir -p configs\necho '{ \n  \"packages\": { \"doas\": \"=6.8.1-r7\" },\n  \"hostname\": \"template.local\"\n}' \u003e configs/my-override.json\n\n\nexport HCLOUD_TOKEN=myHetznerCloudToken\ndocker run -it --rm                     \\\n    -e \"HCLOUD_TOKEN\"                   \\\n    -v \"$(pwd)/configs:/configs\"        \\\n    ghcr.io/mathiaspius/alpine-on-hetzner:latest default.json /configs/my-override.json\n```\n\nThere are a number of optional docker mounts you can use:\n* `/manifests` contains the output manifests from the run.\n* `/cache` used for caching the `apk-tools` package locally between runs.\n* `/configs` used for providing [custom configuration files](#custom-configuration) to builds.\n\n# Custom Configuration\nAny command arguments passed to the docker run invocation will be treated as paths to configuration files to merge into a single combined configuration file which is then fed into the packer build.\n\nThe merge is a \"deep merge\", meaning you can only *add to* or *change* the configuration file not remove from it. If you want to remove a package from the default.json configuration for example you will have to create a copy of it without the package in question and use that as the basis for your build.\n\n### Adding a custom package to your image\nIn order to add a custom package like `nginx` for example you can create the following config file `configs/nginx.json` in your local directory:\n```json\n{ \"packages\": { \"nginx\": \"\" } }\n```\n\u003csup\u003e\u003csub\u003e`packages` is a map where the keys are package names and the value is the version selector. The map is passed directly to an `apk add` command, see [this link](https://wiki.alpinelinux.org/wiki/Package_management#Holding_a_specific_package_back) for version-pinning syntax.\u003c/sub\u003e\u003c/sup\u003e\n\nWhen the container is then run like so:\n```shell\ndocker run -it --rm                     \\\n    -e \"HCLOUD_TOKEN\"                   \\\n    -v \"$(pwd)/configs:/configs\"        \\\n    ghcr.io/mathiaspius/alpine-on-hetzner:latest default.json /configs/nginx.json\n```\nThe package will be appended to `packages` array, like so, immediately before the packer build runs:\n```json\n{\n  (...)\n  \"packages\": {\n    \"openssh\": \"=8.8_p1-r1\",\n    \"syslinux\": \"=6.04_pre1-r9\",\n    \"linux-virt\": \"=5.15.16-r0\",\n    \"cloud-init\": \"@community=21.4-r0\",\n    \"nginx\": \"\"\n  }\n}\n```\n\n# What's in the finished snapshot?\nSee the [default.json](/default.json) config for a list of packages that will be installed into the snapshot if run without any arguments.\n\n[playbook.yml](/playbook.yml) contains the entire ansible playbook used for generating the snapshot.\n[alpine.pkr.hcl](/alpine.pkr.hcl) contains the packer build configuration which uses the playbook above via the [Ansible Provisioner](https://www.packer.io/plugins/provisioners/ansible/ansible)\n\n# How it works\nThe docker image comes with packer, ansible and jq pre-installed (check labels for versions), and builds the [alpine.pkr.hcl](/alpine.pkr.hcl) build against your Hetzner Cloud project using your provided API key. The Packer build will boot a server in rescue mode, then format and install Alpine Linux onto the primary drive of the server. Once done, the server will be saved as a snapshot and shut down. You can then create Alpine Linux servers using the finished snapshot.\n\n# Launching a server\nServers built from the snapshot won't be immediately accessible because the root user is locked by default, but can be configured using the Hetzner interface. Use the following cloud-init config to enable root access and select an ssh key when creating the server to allow login:\n```yaml\n#cloud-config\ndisable_root: false\nusers:\n- name: root\n  lock-passwd: false\n```\n\n# Development\nI have a number of ideas I would like to explore:\n\n- [ ] Re-using or expanding this tool to provision Alpine Linux on dedicated servers, but maintaining the same configuration -interface. I've previously done a less refined version fo this project for dedicated servers [here](https://github.com/MathiasPius/hetzner-zfs-host)\n- [ ] Splitting up configuration files so you can mix-and-match a little more. Would also allow optional *hardened* configurations for example which you could opt into for stricter security.\n- [ ] Creating configuration files for older versions of Alpine Linux.\n- [x] Pipelining alpine-on-hetzner docker image builds and perhaps more importantly..\n- [ ] .. Testing that they work.\n- [x] Add more customization abilities to the configuration file. Being able to enable openrc services with a simple array for example would be simple to implement and very useful.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmathiaspius%2Falpine-on-hetzner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmathiaspius%2Falpine-on-hetzner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmathiaspius%2Falpine-on-hetzner/lists"}