| 1 | --- |
| 2 | title: Setup |
| 3 | description: Install Alchemy and connect it to Hetzner Cloud — create a project, generate an API token, and store it in a profile. |
| 4 | --- |
| 5 | |
| 6 | import { Code, Tabs, TabItem } from "@astrojs/starlight/components"; |
| 7 | import { effectVersion } from "../../../versions"; |
| 8 | |
| 9 | export const pkgs = `"alchemy@latest" "effect@${effectVersion}" "@effect/platform-bun@${effectVersion}" "@effect/platform-node@${effectVersion}"`; |
| 10 | |
| 11 | Everything you need before deploying to Hetzner Cloud: the alchemy |
| 12 | package, a Hetzner project, and an API token stored in a profile. |
| 13 | |
| 14 | ## Prerequisites |
| 15 | |
| 16 | - [Bun](https://bun.sh) or Node.js 22+ |
| 17 | - A [Hetzner Cloud](https://console.hetzner.com) account |
| 18 | |
| 19 | ## Create a Hetzner project |
| 20 | |
| 21 | Everything in Hetzner Cloud — servers, volumes, networks, tokens — |
| 22 | lives inside a **project**. In the |
| 23 | [Hetzner Cloud Console](https://console.hetzner.com): |
| 24 | |
| 25 | 1. Click **+ New project**. |
| 26 | 2. Give it a name (e.g. `my-app`) and open it. |
| 27 | |
| 28 | One project per app (or per environment) is a good default: API |
| 29 | tokens are scoped to a single project, so separate projects give you |
| 30 | hard isolation between apps. |
| 31 | |
| 32 | ## Generate an API token |
| 33 | |
| 34 | Inside the project: |
| 35 | |
| 36 | 1. Go to **Security** → **API tokens**. |
| 37 | 2. Click **Generate API token**. |
| 38 | 3. Give it a description (e.g. `alchemy`) and select |
| 39 | **Read & Write** — Alchemy needs write access to create resources. |
| 40 | 4. Copy the token — Hetzner shows it **only once**. |
| 41 | |
| 42 | ## Create a project directory |
| 43 | |
| 44 | <Tabs syncKey="pkgManager"> |
| 45 | <TabItem label="bun" icon="bun"> |
| 46 | <Code code="mkdir my-app && cd my-app && bun init -y" lang="sh" /> |
| 47 | </TabItem> |
| 48 | <TabItem label="npm" icon="npm"> |
| 49 | <Code code="mkdir my-app && cd my-app && npm init -y" lang="sh" /> |
| 50 | </TabItem> |
| 51 | <TabItem label="pnpm" icon="pnpm"> |
| 52 | <Code code="mkdir my-app && cd my-app && pnpm init" lang="sh" /> |
| 53 | </TabItem> |
| 54 | <TabItem label="yarn" icon="seti:yarn"> |
| 55 | <Code code="mkdir my-app && cd my-app && yarn init -y" lang="sh" /> |
| 56 | </TabItem> |
| 57 | </Tabs> |
| 58 | |
| 59 | ## Install |
| 60 | |
| 61 | <Tabs syncKey="pkgManager"> |
| 62 | <TabItem label="bun" icon="bun"> |
| 63 | <Code code={`bun add ${pkgs}`} lang="sh" /> |
| 64 | </TabItem> |
| 65 | <TabItem label="npm" icon="npm"> |
| 66 | <Code code={`npm install ${pkgs}`} lang="sh" /> |
| 67 | </TabItem> |
| 68 | <TabItem label="pnpm" icon="pnpm"> |
| 69 | <Code code={`pnpm add ${pkgs}`} lang="sh" /> |
| 70 | </TabItem> |
| 71 | <TabItem label="yarn" icon="seti:yarn"> |
| 72 | <Code code={`yarn add ${pkgs}`} lang="sh" /> |
| 73 | </TabItem> |
| 74 | </Tabs> |
| 75 | |
| 76 | ## Connect alchemy to Hetzner |
| 77 | |
| 78 | There is no separate credentials step. The first time you run |
| 79 | `alchemy deploy` (or `plan`, `dev`, `destroy`) on a stack that uses |
| 80 | `Hetzner.providers()`, alchemy prompts for the token you generated |
| 81 | above (and, optionally, an API endpoint override). It is saved under |
| 82 | `~/.alchemy/credentials/<profile>/` in the selected |
| 83 | [profile](/environments/profiles) and reused on subsequent commands. |
| 84 | |
| 85 | In CI (`CI=true`) alchemy skips the prompt and reads `HCLOUD_TOKEN` |
| 86 | (plus an optional `HCLOUD_ENDPOINT`) from the environment instead. |
| 87 | |
| 88 | To re-run the setup later (e.g. to rotate the token, or configure a |
| 89 | separate `prod` profile): |
| 90 | |
| 91 | ```sh |
| 92 | # Re-run the interactive setup (e.g. to rotate the token) |
| 93 | alchemy profile edit --reconfigure Hetzner |
| 94 | |
| 95 | # Or connect Hetzner in a separate `prod` profile |
| 96 | alchemy profile create prod |
| 97 | alchemy profile edit --profile prod --add Hetzner |
| 98 | |
| 99 | # Non-interactive (scripts, agents) |
| 100 | alchemy profile edit --add Hetzner --method stored --set token=env:HCLOUD_TOKEN |
| 101 | ``` |
| 102 | |
| 103 | Inspect what's stored (secrets are redacted): |
| 104 | |
| 105 | ```sh |
| 106 | alchemy profile show |
| 107 | ``` |
| 108 | |
| 109 | :::note[No SSH key setup required] |
| 110 | `Hetzner.Service` deploys your code to a Server over SSH, but you |
| 111 | don't need to create or upload an SSH key for that — alchemy |
| 112 | generates a dedicated deploy key for each Server it creates and |
| 113 | injects it at boot. Adding your own `Hetzner.SshKey` is only for |
| 114 | *your* interactive `ssh` access. |
| 115 | ::: |
| 116 | |
| 117 | ## State storage |
| 118 | |
| 119 | Hetzner has no object-storage state backend of its own, so pick one |
| 120 | of: |
| 121 | |
| 122 | - **`Alchemy.localState()`** — state on disk under `.alchemy/` next |
| 123 | to your code. Zero setup; right for solo projects and trying |
| 124 | things out. |
| 125 | - **A cloud state store** — if you also use Cloudflare or AWS, pass |
| 126 | `Cloudflare.state()` or `AWS.state()` so state is shared with |
| 127 | your team and CI. See [State Store](/state-store). |
| 128 | |
| 129 | State for a Hetzner stack includes each Server's deploy SSH key |
| 130 | (stored redacted), so for shared or production stacks prefer a |
| 131 | remote state store over files on one laptop. |
| 132 | |
| 133 | ## Next steps |
| 134 | |
| 135 | - [Tutorial part 1](/hetzner/tutorial/part-1) — deploy your first |
| 136 | Server. |
| 137 | - [Hetzner overview](/hetzner) — the map of resources and guides. |