Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .github/workflows/tests-deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ jobs:
{"enabled": false, "infra": "azure", "os": "macos-latest", "task": "tests-deployment-infrastructure"},
{"enabled": false, "infra": "exoscale", "os": "ubuntu-latest", "task": "tests-deployment-infrastructure"},
{"enabled": false, "infra": "exoscale", "os": "windows-latest", "task": "tests-deployment-infrastructure"},
{"enabled": true, "infra": "exoscale", "os": "macos-latest", "task": "tests-deployment-infrastructure"}
{"enabled": true, "infra": "exoscale", "os": "macos-latest", "task": "tests-deployment-infrastructure"},
{"enabled": false, "infra": "hetzner", "os": "ubuntu-latest", "task": "tests-deployment-infrastructure"},
{"enabled": false, "infra": "hetzner", "os": "windows-latest", "task": "tests-deployment-infrastructure"},
{"enabled": false, "infra": "hetzner", "os": "macos-latest", "task": "tests-deployment-infrastructure"}
]'

filtered_plan_json="$(jq -c --arg os "${selected_os}" '
Expand Down Expand Up @@ -122,6 +125,12 @@ jobs:
echo "AWS_ACCESS_KEY_ID=${{ secrets.EXOSCALE_API_KEY }}" >> "$GITHUB_ENV"
echo "AWS_SECRET_ACCESS_KEY=${{ secrets.EXOSCALE_API_SECRET }}" >> "$GITHUB_ENV"

- name: Setup Hetzner Cloud credentials
if: ${{ matrix.infra == 'hetzner' }}
shell: bash
run: |
echo "HCLOUD_TOKEN=${{ secrets.HCLOUD_TOKEN }}" >> "$GITHUB_ENV"

# Doing a second build here again. In theory, we could
# upload the build artifact from the ci.yml workflow
# and fetch it here. However, for now the build run time
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Build and dependency directories
/vendor/
/bin/
/tools/bin/

# IDE/editor files
.vscode/
Expand Down
62 changes: 62 additions & 0 deletions HOWTO_SETUP_HETZNER_ACCOUNT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Set up a Hetzner Cloud account for Exasol Personal

This document explains how to set up a Hetzner Cloud account to deploy Exasol Personal.

## ✅ Prerequisites

The following procedure assumes that you have a basic understanding of how Hetzner Cloud works and how to manage projects and API tokens. For more information, refer to the official Hetzner Cloud documentation: https://docs.hetzner.com/cloud/

The Hetzner Cloud project must have sufficient quota to create servers, volumes, networks, and firewalls in the location that you want to use.

## 🛠 Procedure

### 🆕 Create a Hetzner Cloud account

If you do not have a Hetzner Cloud account, visit the Hetzner Cloud home page: https://www.hetzner.com/cloud/ to create one.

In the Hetzner Cloud Console, create a new project or choose an existing one for Exasol Personal deployments.

### 🔐 In the Hetzner Cloud Console, do the following:

1. Open the project that you want to use for Exasol Personal.
2. Navigate to `Security` → `API Tokens`.
3. Click `Generate API Token`.
4. Give the token a descriptive name (e.g., `exasol-personal`).
5. Select **Read & Write** permissions.
6. Click `Generate API Token` and copy the token — it is only shown once.

**Note:** SSH keys are automatically created during deployment — you do not need to pre-add any SSH keys to your Hetzner project.

### 💻 On your local machine, do the following:

1. Set the Hetzner Cloud API token as an environment variable:

```bash
# Linux / macOS (Bash)
export HCLOUD_TOKEN=<your-api-token>
```
```powershell
# Windows (PowerShell)
$env:HCLOUD_TOKEN = "<your-api-token>"
```
```powershell
# Windows (cmd)
set HCLOUD_TOKEN=<your-api-token>
```

2. Run the launcher with the Hetzner preset. The default location is `fsn1`. To deploy to a different location, pass `--location`:

```bash
exasol install hetzner # deploy to fsn1 (default)
exasol install hetzner --location nbg1 # deploy to Nuremberg
exasol install hetzner --location hel1 # deploy to Helsinki
exasol install hetzner --location ash # deploy to Ashburn, US
exasol install hetzner --location hil # deploy to Hillsboro, US
```

Available locations: `fsn1` (Falkenstein), `nbg1` (Nuremberg), `hel1` (Helsinki), `ash` (Ashburn), `hil` (Hillsboro).

For more information on Hetzner Cloud API tokens and authentication, see:

- https://docs.hetzner.com/cloud/api/getting-started/generating-api-token/
- https://docs.hetzner.com/cloud/api/getting-started/
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
- ♾️ **Unlimited Data** — Store and analyze unlimited amounts of data with no artificial limits
- 📈 **Scalable Architecture** — Scale up to any number of nodes using Exasol's MPP (Massively Parallel Processing) architecture
- 🤖 **Built-in AI Functions** — Leverage native AI/ML capabilities with GPU acceleration
- ⚙️ **Simple Deployment** — Spin up a distributed cluster on AWS, Azure, or Exoscale with just a few commands
- ⚙️ **Simple Deployment** — Spin up a distributed cluster on AWS, Azure, Exoscale, or Hetzner Cloud with just a few commands
- 🖥️ **Cross-Platform CLI** — Install and manage your cluster using the Exasol Launcher on Linux, macOS, or Windows


Expand All @@ -35,6 +35,7 @@ A cloud account on one of the supported platforms with permission to provision c
- **AWS** — [Set up an AWS account for Exasol Personal](./HOWTO_SETUP_AWS_ACCOUNT.md)
- **Azure** — [Set up an Azure account for Exasol Personal](./HOWTO_SETUP_AZURE_ACCOUNT.md)
- **Exoscale** — [Set up an Exoscale account for Exasol Personal](./HOWTO_SETUP_EXOSCALE_ACCOUNT.md)
- **Hetzner Cloud** — [Set up a Hetzner Cloud account for Exasol Personal](./HOWTO_SETUP_HETZNER_ACCOUNT.md)


## 🏎️ Quick Start (macOS / Linux)
Expand Down Expand Up @@ -63,6 +64,10 @@ exasol install azure # Microsoft Azure
exasol install exoscale # Exoscale
```

```bash
exasol install hetzner # Hetzner Cloud
```

Read on for Windows instructions and full details.


Expand Down Expand Up @@ -92,6 +97,7 @@ Read on for Windows instructions and full details.
exasol install aws # Amazon Web Services
exasol install azure # Microsoft Azure
exasol install exoscale # Exoscale
exasol install hetzner # Hetzner Cloud
```
The `exasol install` command does the following:
- Generates OpenTofu files in the deployment directory
Expand Down
115 changes: 115 additions & 0 deletions assets/infrastructure/hetzner/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 60 additions & 0 deletions assets/infrastructure/hetzner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Hetzner Cloud Infrastructure as Code Architecture

## Overview
This document describes the Infrastructure as Code (IaC) implementation for Exasol Personal on Hetzner Cloud. It supports both single-node and multi-node (cluster) deployments with a simple, opinionated setup for networking, storage, and installation.

## Prerequisites and Hetzner Cloud Provider
- Hetzner Cloud API token is taken from the environment variable `HCLOUD_TOKEN`.
- Location selection is available via `--location` (e.g., `fsn1`, `nbg1`, `hel1`, `ash`, `hil`). Defaults to `fsn1` (Falkenstein).
- The provider configuration in `providers.tf` uses the environment variable and does not define credentials inline.

## Infrastructure Components

### Compute
- Hetzner Cloud servers named after their node IDs (e.g., `n11`, `n12`, ...).
- Default server type `ccx33` (8 vCPU / 16 GB RAM; configurable via `--server-type`).
- Ubuntu 22.04 image from Hetzner's public image catalog.
- Cluster support: one server per node; controlled by `--cluster-size` (default: 1).
- Servers get public IPv4 addresses automatically.

### Storage
- Separate volumes for OS/root and database data.
- OS disk is part of the server (size configurable via `--os-volume-size`, minimum 20 GB).
- Data volumes are separate block storage volumes attached via `hcloud_volume_attachment`.
- Data volume size configurable via `--data-volume-size` (in GB).
- Remote archive volume on Hetzner Object Storage (S3-compatible) can be enabled via `--s3-archive-enabled` (default: disabled). Available in EU locations only.

### Networking
- Servers receive public IPv4 addresses automatically.
- A private network (`hcloud_network`) with a subnet is created for inter-node communication.
- Private IPs are assigned from the `172.16.0.0/24` range starting at `.20`.
- A firewall (`hcloud_firewall`) controls inbound/outbound traffic.

## Access and Security
The following ports are exposed via firewall rules:

1. 22 — SSH access (public)
2. 2581 — Default bucketfs (public)
3. 8443 — Admin UI HTTPS (public)
4. 8563 — Default database port (public)
5. 20002 — Exasol container SSH (public)
6. 20003 — Exasol confd API (public)

Additionally, internal traffic (TCP/UDP/ICMP) is allowed between cluster nodes via the private network CIDR range.

## Resource Organization (Labels)
- A unique deployment ID is generated at apply time (e.g., `exasol-<deployment_id>`).
- All resources carry labels: `managed_by=opentofu`, `project=exasol-personal`, `deployment_id=exasol-<id>`.
- The cleanup tool uses these labels to discover and manage deployments.

## Power State Management

`exasol stop` and `exasol start` control VM power state directly via the Hetzner Cloud API using the server IDs stored in `deployment.json`. No Terraform resources are involved in stop/start — the Go launcher calls the Hetzner API (`/v1/servers/{id}/actions/shutdown` and `/v1/servers/{id}/actions/poweron`) and waits for the action to complete. This avoids Terraform state drift that would occur with provisioner-based approaches.

`HCLOUD_TOKEN` must be set in the environment when running `exasol stop` or `exasol start`.

## Deployment Artifacts
After a successful apply, the following files are written to the deployment directory:
- `deployment.json` — connection info (IPs, ports, instance IDs)
- `secrets.json` — credentials (DB password, Admin UI password)
- `node_access.pem` — SSH private key for node access
28 changes: 28 additions & 0 deletions assets/infrastructure/hetzner/cloudconf/10-create-ubuntu-user.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#cloud-config
# Hetzner Ubuntu images use 'root' as the default user, but the Exasol
# installation scripts expect a 'ubuntu' user. Create it here.

# The public SSH key is injected via cloud-config to avoid timing issues
# with Hetzner's SSH key injection mechanism.
ssh_authorized_keys:
- ${admin_ssh_public_key}

users:
- name: ubuntu
gecos: Ubuntu User
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
groups: sudo
ssh_authorized_keys:
- ${admin_ssh_public_key}

# Populate known_hosts so the barrier client can SSH to itself without host key prompts.
# The private network may not be up immediately, so retry with a delay.
runcmd:
- |
for i in $(seq 1 30); do
ssh-keyscan -H 172.16.0.20 >> /home/ubuntu/.ssh/known_hosts 2>/dev/null && break
sleep 2
done
- "ssh-keyscan -H localhost >> /home/ubuntu/.ssh/known_hosts 2>/dev/null || true"
- "chown ubuntu:ubuntu /home/ubuntu/.ssh/known_hosts"
Loading