{"id":15428936,"url":"https://github.com/timdaub/hetzner-cloud-deploy-server-action","last_synced_at":"2025-04-19T15:27:23.239Z","repository":{"id":54559586,"uuid":"321357276","full_name":"TimDaub/hetzner-cloud-deploy-server-action","owner":"TimDaub","description":"Deploy a Hetzner Cloud Server from a GitHub Action.","archived":false,"fork":false,"pushed_at":"2024-01-19T15:40:38.000Z","size":92,"stargazers_count":37,"open_issues_count":7,"forks_count":14,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-14T23:36:07.856Z","etag":null,"topics":["actions","cloud","continous","github","hetzner","integration"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TimDaub.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":"2020-12-14T13:32:31.000Z","updated_at":"2025-04-02T20:04:11.000Z","dependencies_parsed_at":"2024-10-20T19:07:20.943Z","dependency_job_id":null,"html_url":"https://github.com/TimDaub/hetzner-cloud-deploy-server-action","commit_stats":{"total_commits":53,"total_committers":3,"mean_commits":"17.666666666666668","dds":0.07547169811320753,"last_synced_commit":"aa52a29020381f574438dd53ec224e906e964c65"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimDaub%2Fhetzner-cloud-deploy-server-action","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimDaub%2Fhetzner-cloud-deploy-server-action/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimDaub%2Fhetzner-cloud-deploy-server-action/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimDaub%2Fhetzner-cloud-deploy-server-action/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TimDaub","download_url":"https://codeload.github.com/TimDaub/hetzner-cloud-deploy-server-action/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249726326,"owners_count":21316388,"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":["actions","cloud","continous","github","hetzner","integration"],"created_at":"2024-10-01T18:08:04.391Z","updated_at":"2025-04-19T15:27:23.220Z","avatar_url":"https://github.com/TimDaub.png","language":"JavaScript","funding_links":[],"categories":["Integrations"],"sub_categories":["Rust"],"readme":"# Hetzner Cloud Deploy GitHub Action\n\n\u003e Deploy a [Hetzner](https://hetzner.cloud/?ref=zHBLL3AHXP0S) Cloud Server from a GitHub Action.\n\n[Hetzner](https://hetzner.cloud/?ref=zHBLL3AHXP0S) is a [zero-carbon\ninfrastructure\nprovider](https://github.com/vrde/notes/tree/master/zero-carbon).\n\n## Usage\n\nSee [action.yml](./action.yml).\n\nBasic:\n\n```yml\njobs:\n  build:\n    runs-on: Ubuntu-20.04\n    steps:\n      - uses: TimDaub/hetzner-cloud-deploy-server-action@v2\n        with:\n          server-name: \"gh-actions-server\"\n          server-image: \"ubuntu-20.04\"\n          server-type: \"cx11\"\n          server-location: \"nbg1\"\n          ssh-key-name: \"my key name\"\n          hcloud-token: ${{ secrets.HCLOUD_TOKEN }}\n```\n\n\n1.  [Create a Hetzner Account](https://hetzner.cloud/?ref=zHBLL3AHXP0S)\n1.  Visit the Hetzner Cloud Console at\n    [console.hetzner.cloud](https://console.hetzner.cloud/), select your\n    project, and create a new Read \u0026 Write API token (\"Security\" =\u003e \"API\n    Tokens\").\n1. In the \"Security\" tab in the Hetzner Cloud Console, you can check your ssh\n   key's name.\n1. Add the Hetzner API token to your GitHub repositories secrets (\"Settings\" =\u003e\n   \"Secrets\") as `HCLOUD_TOKEN`.\n1. To know which `server-images` and `server-types` Hetzner provides, check the\n   [FAQ](#FAQ).\n\n### Notes\n\n- `server-name` MUST NOT contain spaces.\n- If you don't want the server to be deleted after the action has run, add\n`delete-server: false` as an input in your workflow\n- `ssh-key-name`'s value should be the name of the SSH key as recorded in the Hetzner's cloud console (Under Security -\u003e SSH Keys)\n- The server's ipv4 is available to subsequent steps by accessing the env\nvariable `SERVER_IPV4`.\n- By default, the action queries the to-be-launched server's port 22 (SSH) for\nmaximally 20 seconds (`startup-timeout`). Continous steps are only run if the\nserver has responded within this time.\n- `startup-timeout` (milliseconds) can be adjusted manually. For a `cx11`\ninstance, I recommend at least 20 seconds bootup time.\n- To assign a [Floating IP](https://docs.hetzner.cloud/#floating-ips) the input\n  `floating-ip-id` can be set. See FAQ for [instruction about how to get a\n  floating IP's id](#how-do-i-get-the-id-of-a-floating-ip).\n- When assigning an instance to a floating IP, the [Hetzner\n  recommends](https://docs.hetzner.com/cloud/floating-ips/faq/) configuring a\n  temporary or permanent IPV4 on the machine.\n- After a server has been successfully assigned a floating IP, it is exported\n  as an environment variable called `SERVER_FLOATING_IPV4`.\n\n## FAQ\n\n### How do I get all possible images to build from?\n\nYou can use the Hetzner Cloud\n[API](https://docs.hetzner.cloud/#images-get-all-images).  The following `curl`\ncommand works well with [jq](https://github.com/stedolan/jq):\n\n```bash\n$ curl \\\n  -H \"Authorization: Bearer $API_TOKEN\" \\\n  'https://api.hetzner.cloud/v1/images' | jq '.images[].name'\n\n\"ubuntu-16.04\"\n\"debian-9\"\n\"centos-7\"\n\"ubuntu-18.04\"\n\"debian-10\"\n\"centos-8\"\n\"ubuntu-20.04\"\n\"fedora-32\"\n\"fedora-33\"\n```\n\n### How do I get all possible server types?\n\nYou can use the Hetzner Cloud\n[API](https://docs.hetzner.cloud/#server-types-get-all-server-types).  The\nfollowing `curl` command works well with [jq](https://github.com/stedolan/jq):\n\n```bash\n$ curl \\\n  -H \"Authorization: Bearer $API_TOKEN\" \\\n  'https://api.hetzner.cloud/v1/server_types' | jq '.server_types[].name'\n\n\"cx11\"\n\"cx11-ceph\"\n\"cx21\"\n\"cx21-ceph\"\n\"cx31\"\n\"cx31-ceph\"\n\"cx41\"\n\"cx41-ceph\"\n\"cx51\"\n\"cx51-ceph\"\n\"ccx11\"\n\"ccx21\"\n\"ccx31\"\n\"ccx41\"\n\"ccx51\"\n\"cpx11\"\n\"cpx21\"\n\"cpx31\"\n\"cpx41\"\n\"cpx51\"\n```\n\n### How do I get all possible locations?\n\nYou can use the Hetzner Cloud\n[API](https://docs.hetzner.cloud/#server-types-get-all-server-types).  The\nfollowing `curl` command works well with [jq](https://github.com/stedolan/jq):\n\n```bash\n$ curl \\\n  -H \"Authorization: Bearer $API_TOKEN\" \\\n  'https://api.hetzner.cloud/v1/locations' | jq '.locations[].name'\n\n\"fsn1\"\n\"nbg1\"\n\"hel1\"\n\"ash\"\n\"hil\"\n```\n\n\n### How do I get the ID of a Floating IP?\n\nYou can use the Hetzner Cloud\n[API](https://docs.hetzner.cloud/#floating-ips-get-all-floating-ips).  The\nfollowing `curl` command works well with [jq](https://github.com/stedolan/jq):\n\n```bash\n$ curl -s \\\n  -H \"Authorization: Bearer $API_TOKEN\" \\\n  'https://api.hetzner.cloud/v1/floating_ips' | jq '.floating_ips[].id'\n1\n2\n3\n```\n\n### Why is this action useful?\n\nIn combination with\n[webfactory/ssh-agent](https://github.com/webfactory/ssh-agent) you can use it\nto provision a Hetzner Cloud instance completely from within a GitHub Action. An\nexample:\n\n```yml\njobs:\n  build:\n    runs-on: Ubuntu-20.04\n    steps:\n      - uses: TimDaub/hetzner-cloud-deploy-server-action@v2\n        with:\n          server-name: \"gh-actions-server\"\n          server-image: \"ubuntu-20.04\"\n          server-type: \"cx11\"\n          server-location: \"nbg1\"\n          ssh-key-name: \"my key name\"\n          hcloud-token: ${{ secrets.HCLOUD_TOKEN }}\n      - uses: webfactory/ssh-agent@v0.4.1\n        with:\n          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}\n      - run: mkdir -p ~/.ssh/ \u0026\u0026 ssh-keyscan -H $SERVER_IPV4 \u003e\u003e ~/.ssh/known_hosts\n      - run: ssh root@$SERVER_IPV4 touch tim_was_here\n      - run: ssh root@$SERVER_IPV4 ls\n```\n\nAfter all steps have run, your provisioned Hetzner instance gets shutdown by\nthe cleanup script.\n\n### How do I use this Action with e.g. a Domain Name?\n\nAll Hetzner servers created with this Action get assigned an arbitrary IP, which\ncan make it difficult to immediately set it as an A-record on a domain you own.\n\nHetzner, however, provides a feature called [Floating\nIPs](https://docs.hetzner.com/cloud/floating-ips/faq/), that allows a user to\nassign a static IP to a server.\n\nBy setting your DNS A-record to a floating IP and adding its ID as an input,\nyou can hence make your launched server predictably-addressable.\n\nBe aware that the floating IP needs to be in the same location as the server. To \nachieve this, use the additional parameter `server-location`. \n\nSpecifically, working with floating IPs can get a bit messy. Here's an example\nconfiguration.\n\n```yml\njobs:\n  build:\n    runs-on: Ubuntu-20.04\n    steps:\n      - uses: TimDaub/hetzner-cloud-deploy-server-action@v2\n        with:\n          server-name: \"server\"\n          server-image: \"ubuntu-20.04\"\n          server-type: \"cx11\"\n          server-location: \"fsn1\"\n          ssh-key-name: \"my key name\"\n          hcloud-token: ${{ secrets.HCLOUD_TOKEN }}\n          startup-timeout: 40000\n          floating-ip-id: my-id\n          floating-ip-assignment-timeout: 30000\n      - uses: webfactory/ssh-agent@v0.4.1\n        with:\n          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}\n      - run: mkdir -p ~/.ssh/ \u0026\u0026 ssh-keyscan -H $SERVER_IPV4 \u003e\u003e ~/.ssh/known_hosts\n      - run: ssh root@$SERVER_IPV4 \"ip addr add $SERVER_FLOATING_IPV4 dev eth0\"\n      - run: mkdir -p ~/.ssh/ \u0026\u0026 ssh-keyscan -H $SERVER_FLOATING_IPV4 \u003e\u003e ~/.ssh/known_hosts\n      - run: ssh root@$SERVER_FLOATING_IPV4 touch tim_was_here\n      - run: ssh root@$SERVER_FLOATING_IPV4 ls\n\n```\n\nNote how we use `ssh-keyscan` twice here to configure the GitHub Action server\nfor once with the `SERVER_IPV4` but then later also with the\n`SERVER_FLOATING_IPV4`.\nThis specific step is optional, but it allows a subsequent step to directly\nconnect to the floating IP.\n\n### Can I lose money when running this script?\n\n**Yes, you certainly can.**\n\nThere may be instances where something within my\nscript's cleanup fails and the instance remains online. So if you're planning\nto run your tests many times or if you're planning to launch huge instances,\nplease make sure to double check if some instances remain running after the\naction has completed.\n\nAlso, please note that [Hetzner bills per hours, not minutes](https://docs.hetzner.com/cloud/billing/faq/#how-do-you-bill-your-servers). This means that a 30 second run will be billed as a 1 hour run, a 61 minute run will be billed as a 2 hour run, etc. ([#14](https://github.com/TimDaub/hetzner-cloud-deploy-server-action/issues/14))\n\nIf you push very frequently, you might want to ensure that your workflow requires explicit approval before running by using [GitHub's Environment approval mechanism](https://web.archive.org/web/20210209175158/https://www.aaron-powell.com/posts/2021-01-11-using-environments-for-approval-workflows-with-github/).\n\nYou have been warned.\n\n### What do these errors mean?\n- `When sending the request to Hetzner's API, an error occurred \"Unprocessable Entity\"`\n  - The data you are passing is syntactically correct, but Hetzner could not achieve the task. Make sure all your secrets are set correctly, and that your `ssh-key-name` is the same as in the Hetzner Cloud interface.\n\n## License\n\nSee [License](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimdaub%2Fhetzner-cloud-deploy-server-action","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimdaub%2Fhetzner-cloud-deploy-server-action","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimdaub%2Fhetzner-cloud-deploy-server-action/lists"}