Getting Started
-
Connecting hstacks to Hetzner Cloud
hstacks is an Infrastructure as Code (IaC) platform for creating, managing, and deleting Stacks in a Hetzner Cloud account.
A Stack is a collection of one or more Cloud resources (Servers, Volumes, Firewalls, etc) which are described in a JSON file.
A hstacks token is associated with a user. It's a means to authenticate and interact with a hstacks account.
A hstacks key contains a reference to an encrypted Hetzner Cloud API Key.
Hetzner Cloud API Keys are encrypted with 4096-bit encryption keys, and are never displayed once stored.
A User has a hstacks Token...
A hstacks Token has a hstacks Key...
A hstacks Key contains a Hetzner Cloud API Key...
Which hstacks uses to deploy Stacks in your Hetzner Cloud account.
Stacks, Tokens, and Keys can be created and destroyed through the hstacks API: https://api.hstacks.dev
Retrieve a list of available images for a specified architecture. If no architecture is specified, all available images are returned.
Response: JSON object containing available images.
{ "data": [ { "name": "ubuntu-20.04", "architecture": "x86" }, { "name": "debian-11", "architecture": "x86" }, { "name": "rocky-8", "architecture": "x86" }, { "name": "centos-stream-9", "architecture": "x86" }, { "name": "ubuntu-22.04", "architecture": "x86" }, { "name": "rocky-9", "architecture": "x86" }, { "name": "centos-stream-9", "architecture": "arm" }, { "name": "debian-11", "architecture": "arm" }, { "name": "rocky-8", "architecture": "arm" }, { "name": "rocky-9", "architecture": "arm" }, { "name": "ubuntu-20.04", "architecture": "arm" }, { "name": "ubuntu-22.04", "architecture": "arm" }, { "name": "alma-8", "architecture": "x86" }, { "name": "alma-9", "architecture": "x86" }, { "name": "alma-8", "architecture": "arm" }, { "name": "alma-9", "architecture": "arm" }, { "name": "debian-12", "architecture": "x86" }, { "name": "debian-12", "architecture": "arm" }, { "name": "fedora-40", "architecture": "x86" }, { "name": "fedora-40", "architecture": "arm" }, { "name": "ubuntu-24.04", "architecture": "x86" }, { "name": "ubuntu-24.04", "architecture": "arm" }, { "name": "fedora-41", "architecture": "x86" }, { "name": "fedora-41", "architecture": "arm" } ], "msg": "Stack creation initiated", "status": "ok" }
Example cURL Request:
curl --location --request GET 'https://api.hstacks.dev/info/available-images' --header 'Content-Type: application/json' --header 'Access-Token: <YOUR_HSTACKS_TOKEN>'
Fetch a list of valid locations for a given server type. If no server type is provided, all locations are returned.
Response: JSON object containing available locations.
{ "data": [ { "name": "fsn1", "description": "Falkenstein DC Park 1", "country": "DE", "city": "Falkenstein", "latitude": 50.47612, "longitude": 12.370071, "networkZone": "eu-central" }, { "name": "nbg1", "description": "Nuremberg DC Park 1", "country": "DE", "city": "Nuremberg", "latitude": 49.452102, "longitude": 11.076665, "networkZone": "eu-central" }, { "name": "hel1", "description": "Helsinki DC Park 1", "country": "FI", "city": "Helsinki", "latitude": 60.169855, "longitude": 24.938379, "networkZone": "eu-central" }, { "name": "ash", "description": "Ashburn, VA", "country": "US", "city": "Ashburn, VA", "latitude": 39.045821, "longitude": -77.487073, "networkZone": "us-east" }, { "name": "hil", "description": "Hillsboro, OR", "country": "US", "city": "Hillsboro, OR", "latitude": 45.54222, "longitude": -122.951924, "networkZone": "us-west" }, { "name": "sin", "description": "Singapore", "country": "SG", "city": "Singapore", "latitude": 1.283333, "longitude": 103.833333, "networkZone": "ap-southeast" } ], "msg": "Available locations retrieved.", "status": "ok" }
Example cURL Request:
curl --location --request GET 'https://api.hstacks.dev/info/available-locations' --header 'Content-Type: application/json' --header 'Access-Token: '<YOUR_HSTACKS_TOKEN>'
Retrieve a list of server types available in a specific location. If no location is provided, all server types are returned.
Response: JSON object containing server types.
{ "data": [ { "name": "cpx11", "description": "CPX 11", "architecture": "x86" }, { "name": "cpx21", "description": "CPX 21", "architecture": "x86" }, { "name": "cpx31", "description": "CPX 31", "architecture": "x86" }, { "name": "cpx41", "description": "CPX 41", "architecture": "x86" }, { "name": "cpx51", "description": "CPX 51", "architecture": "x86" }, { "name": "cax11", "description": "CAX11", "architecture": "arm" }, { "name": "cax21", "description": "CAX21", "architecture": "arm" }, { "name": "cax31", "description": "CAX31", "architecture": "arm" }, { "name": "cax41", "description": "CAX41", "architecture": "arm" }, { "name": "ccx13", "description": "CCX13 Dedicated CPU", "architecture": "x86" }, { "name": "ccx23", "description": "CCX23 Dedicated CPU", "architecture": "x86" }, { "name": "ccx33", "description": "CCX33 Dedicated CPU", "architecture": "x86" }, { "name": "ccx43", "description": "CCX43 Dedicated CPU", "architecture": "x86" }, { "name": "ccx53", "description": "CCX53 Dedicated CPU", "architecture": "x86" }, { "name": "ccx63", "description": "CCX63 Dedicated CPU", "architecture": "x86" }, { "name": "cx22", "description": "CX22", "architecture": "x86" }, { "name": "cx32", "description": "CX32", "architecture": "x86" }, { "name": "cx42", "description": "CX42", "architecture": "x86" }, { "name": "cx52", "description": "CX52", "architecture": "x86" } ], "msg": "Available locations retrieved.", "status": "ok" }
Example cURL Request:
curl --location --request GET 'https://api.hstacks.dev/info/available-servers' --header 'Content-Type: application/json' --header 'Access-Token: '<YOUR_HSTACKS_TOKEN>'
Create a new hstacks Key.
Request Body: JSON object containing `value` (Hetzner Cloud API Key) and `name` (optional key name).
{ "name" : "eph-key", "value" : "<HETZNER API KEY>" }
Create a new hstacks Token associated with an existing Key.
Request Body: JSON object containing `key` (UUID of the key) and `name` (optional token name).
{ "name" : "Hetzer Token", "key" : "<HSTACKS KEY ID>" }
Create a new Stack from a JSON description.
Request Body: JSON object defining the Stack.
// An empty example stack { "name": "example-stack", "servers": [], "firewalls": [], "volumes": [], "successHook": "https://example.org/success", "errorHook": "https://example.org/error" }
Response: JSON object with the ID of the created Stack.
{ "data": "<NEW STACK ID>", "msg": "Stack creation initiated", "status": "ok" }
Example cURL Request:
curl --location --request POST 'http://api.hstacks.dev/stacks/create' --header 'Content-Type: application/json' --header 'Access-Token: <YOUR_HSTACKS_TOKEN>' --data-raw '{ "name": "example-stack", "servers": [], "firewalls": [], "volumes": [], "successHook": "https://example.org/success", "errorHook": "https://example.org/error" }'
Check the status of an existing Stack by its ID.
Path Parameter: `stackID` - The unique identifier of the Stack to check.
Response: JSON object with the current status of the Stack.
// Example response for a successful status retrieval { "status": "ok", "msg": "Status successfully retrieved for Stack '<stackID>'", "data": "ACTIVE" } // Example response for a missing or unauthorized Stack { "status": "err", "msg": "Could not find Stack with ID: '<stackID>'" }
Delete an existing Stack by its ID.
Response: JSON object confirming Stack deletion initiation.
{ "data": "<stackID>", "msg": "Stack deletion initiated.", "status": "ok" }
Example cURL Request:
curl --location --request POST 'http://api.hstacks.dev/stacks/delete/<STACK_ID>' --header 'Content-Type: application/json' --header 'Access-Token: <YOUR_HSTACKS_TOKEN>'
A few example Stacks to get you started. All of these Stack Templates can be POSTed as with a JSON body tohttps://api.hstacks.dev/api/stacks/createwith a hstacks ACCESS_TOKEN header to deploy the resources deployed within.
This stack will deploy a single cax11 server in the Falkenstein region with Ubuntu 24.04. It will also create a Firewall that enables SSH access and add it to the server.
There is no SSH key added to the server, so Hetzner will email you credentials to SSH into your server.
Stack Template:
{ "name" : "my-first-stack", "servers": [ { "name": "my-first-server", "serverType": "cax11", "image": "ubuntu-24.04", "location": "fsn1", "labels": { "project": "demoProject" }, "startAfterCreate": true, "firewalls": [ "ssh" ], "startScript": "#!/bin/bash\nsudo apt update\nsudo apt upgrade -y" } ], "firewalls": [ { "name": "ssh", "rules": [ { "direction": "inbound", "protocol": "tcp", "port": 22, "sourceIPs": [ "0.0.0.0/0", " ::/0" ], "description": "Enable SSH access to a server." } ], "labels": {} } ], "volumes": [] }
Example cURL Request:
curl --location --request POST 'https://api.hstacks.dev/api/stacks/create' --header 'Content-Type: application/json' --header 'Access-Token:' --data-raw '{ "name" : "my-first-stack", "servers": [ { "name": "my-first-server", "serverType": "cax11", "image": "ubuntu-24.04", "location": "fsn1", "labels": { "project": "demoProject" }, "startAfterCreate": true, "firewalls": [ "ssh" ], "startScript": "#!/bin/bash\nsudo apt update\nsudo apt upgrade -y" } ], "firewalls": [ { "name": "ssh", "rules": [ { "direction": "inbound", "protocol": "tcp", "port": 22, "sourceIPs": [ "0.0.0.0/0", " ::/0" ], "description": "Enable SSH access to a server." } ], "labels": {} } ], "volumes": [] }'
This stack will deploy a single cx22 server in the Helsinki region with Ubuntu 24.04 that will run a public HTTP server with NGINX. It will also create a Firewall that enables SSH access and add it to the server.
There is no SSH key added to the server, so Hetzner will email you credentials to SSH into your server.
Stack Template:
{ "errorHook": "", "firewalls": [ { "labels": {}, "name": "http-inbound", "rules": [ { "description": "Allows HTTP connections over port 80.", "direction": "inbound", "port": 80, "protocol": "tcp", "sourceIPs": [ "0.0.0.0/0", " ::/0" ] } ] } ], "name": "HTTP-Server-Stack", "servers": [ { "environmentVariables": {}, "firewalls": [ "http-inbound" ], "image": "ubuntu-24.04", "labels": {}, "location": "hel1", "name": "my-http-server", "serverType": "cx22", "sshKeyName": "", "startAfterCreate": true, "startScript": "#!/bin/bash\n# Update package index\napt-get update -y\n\n# Install NGINX\napt-get install -y nginx\n\n# Enable and start NGINX on boot\nsystemctl enable nginx\nsystemctl start nginx\n\n# Optional: Create a custom index.html for demonstration\necho \"You've just deployed a HTTP server with hstacks! on Ubuntu 24.04!
\" > /var/www/html/index.html\n" } ], "successHook": "", "volumes": [] }
Example cURL Request:
curl --location --request POST 'https://api.hstacks.dev/api/stacks/create' --header 'Content-Type: application/json' --header 'Access-Token:' --data-raw '{"errorHook":"","firewalls":[{"labels":{},"name":"http-inbound","rules":[{"description":"Allows HTTP connections over port 80.","direction":"inbound","port":80,"protocol":"tcp","sourceIPs":["0.0.0.0/0"," ::/0"]}]}],"name":"HTTP-Server","servers":[{"environmentVariables":{},"firewalls":["http-inbound"],"image":"ubuntu-24.04","labels":{},"location":"hel1","name":"my-http-server","serverType":"cx22","sshKeyName":"","startAfterCreate":true,"startScript":"#!/bin/bash\n# Update package index\napt-get update -y\n\n# Install NGINX\napt-get install -y nginx\n\n# Enable and start NGINX on boot\nsystemctl enable nginx\nsystemctl start nginx\n\n"}],"successHook":"","volumes":[]}'
This stack will deploy a single cx22 server in the Helsinki region with Ubuntu 24.04 that will install and configure a WordPress instance. It will also create a Firewall that enables HTTP access and add it to the server.
There is no SSH key added to the server, so Hetzner will email you credentials to SSH into your server.
Stack Template:
{ "errorHook": "", "firewalls": [ { "name": "http-access", "rules": [ { "direction": "inbound", "protocol": "tcp", "port": 80, "sourceIPs": [ "0.0.0.0/0", " ::/0" ], "description": "Enable HTTP access" } ], "labels": {} } ], "name": "wordpress-stack", "servers": [ { "environmentVariables": {}, "firewalls": [ "http-access" ], "image": "ubuntu-24.04", "labels": {}, "location": "hel1", "name": "wp-runner", "serverType": "cax11", "sshKeyName": "seans-ssh-key", "startAfterCreate": true, "startScript": "#!/bin/bash\n\nset -e\n\n# Redirect output to a log file for debugging\nexec > /var/log/userdata.log 2>&1\n\n# Update and upgrade the system\necho \"Updating system...\"\napt update && apt upgrade -y\n\n# Install necessary packages\necho \"Installing required packages...\"\nDEBIAN_FRONTEND=noninteractive apt install -y apache2 mysql-server php libapache2-mod-php php-mysql curl unzip\n\n# Configure MySQL securely without interaction\necho \"Configuring MySQL securely...\"\ndebconf-set-selections <<< \"mysql-server mysql-server/root_password password root_password\"\ndebconf-set-selections <<< \"mysql-server mysql-server/root_password_again password root_password\"\n\n# Ensure MySQL service is running\nsystemctl enable mysql\nsystemctl start mysql\n\n# Create a MySQL database and user for WordPress\nDB_NAME=\"wordpress\"\nDB_USER=\"wordpress_user\"\nDB_PASS=\"secure_password\"\n\necho \"Creating MySQL database and user...\"\nmysql -uroot -proot_password <$VHOST_CONF\n \n ServerAdmin [email protected]\n DocumentRoot /var/www/html/wordpress\n ServerName example.com\n ServerAlias www.example.com\n\n \nEOL\n\na2ensite wordpress.conf\na2dissite 000-default.conf\nsystemctl reload apache2\n\n# Final message\necho \"WordPress installation complete! Please visit your server's IP address or domain name to finish the setup.\"\n" } ], "successHook": "", "volumes": [] }\n AllowOverride All\n \n\n ErrorLog \\${APACHE_LOG_DIR}/error.log\n CustomLog \\${APACHE_LOG_DIR}/access.log combined\n
Example cURL Request:
curl --location --request POST 'https://api.hstacks.dev/api/stacks/create' --header 'Content-Type: application/json' --header 'Access-Token: <YOUR_HSTACKS_TOKEN>' --data-raw '{ "errorHook": "", "firewalls": [ { "name": "http-access", "rules": [ { "direction": "inbound", "protocol": "tcp", "port": 80, "sourceIPs": [ "0.0.0.0/0", " ::/0" ], "description": "Enable HTTP access" } ], "labels": {} } ], "name": "wordpress-stack", "servers": [ { "environmentVariables": {}, "firewalls": [ "http-access" ], "image": "ubuntu-24.04", "labels": {}, "location": "hel1", "name": "wp-runner", "serverType": "cax11", "sshKeyName": "seans-ssh-key", "startAfterCreate": true, "startScript": "#!/bin/bash\n\nset -e\n\n# Redirect output to a log file for debugging\nexec > /var/log/userdata.log 2>&1\n\n# Update and upgrade the system\necho \"Updating system...\"\napt update && apt upgrade -y\n\n# Install necessary packages\necho \"Installing required packages...\"\nDEBIAN_FRONTEND=noninteractive apt install -y apache2 mysql-server php libapache2-mod-php php-mysql curl unzip\n\n# Configure MySQL securely without interaction\necho \"Configuring MySQL securely...\"\ndebconf-set-selections <<< \"mysql-server mysql-server/root_password password root_password\"\ndebconf-set-selections <<< \"mysql-server mysql-server/root_password_again password root_password\"\n\n# Ensure MySQL service is running\nsystemctl enable mysql\nsystemctl start mysql\n\n# Create a MySQL database and user for WordPress\nDB_NAME=\"wordpress\"\nDB_USER=\"wordpress_user\"\nDB_PASS=\"secure_password\"\n\necho \"Creating MySQL database and user...\"\nmysql -uroot -proot_password <<MYSQL_SCRIPT\nCREATE DATABASE $DB_NAME;\nCREATE USER '\''$DB_USER'\''@'\''localhost'\'' IDENTIFIED BY '\''$DB_PASS'\'';\nGRANT ALL PRIVILEGES ON $DB_NAME.* TO '\''$DB_USER'\''@'\''localhost'\'';\nFLUSH PRIVILEGES;\nMYSQL_SCRIPT\n\necho \"Database $DB_NAME and user $DB_USER created with password $DB_PASS.\"\n\n# Download and configure WordPress\necho \"Downloading WordPress...\"\ncd /tmp\ncurl -O https://wordpress.org/latest.zip\nunzip latest.zip\n\n# Move WordPress files to the web directory\necho \"Configuring WordPress files...\"\nmv wordpress /var/www/html/wordpress\nchown -R www-data:www-data /var/www/html/wordpress\nchmod -R 755 /var/www/html/wordpress\n\n# Create WordPress config file\ncd /var/www/html/wordpress\ncp wp-config-sample.php wp-config.php\nsed -i \"s/database_name_here/$DB_NAME/\" wp-config.php\nsed -i \"s/username_here/$DB_USER/\" wp-config.php\nsed -i \"s/password_here/$DB_PASS/\" wp-config.php\n\n# Enable Apache2 rewrite module and restart the server\necho \"Enabling Apache2 rewrite module...\"\na2enmod rewrite\nsystemctl restart apache2\n\n# Create Apache virtual host for WordPress\nVHOST_CONF=\"/etc/apache2/sites-available/wordpress.conf\"\necho \"Creating Apache virtual host for WordPress...\"\ncat <<EOL > $VHOST_CONF\n<VirtualHost *:80>\n ServerAdmin [email protected]\n DocumentRoot /var/www/html/wordpress\n ServerName example.com\n ServerAlias www.example.com\n\n <Directory /var/www/html/wordpress/>\n AllowOverride All\n </Directory>\n\n ErrorLog \\${APACHE_LOG_DIR}/error.log\n CustomLog \\${APACHE_LOG_DIR}/access.log combined\n</VirtualHost>\nEOL\n\na2ensite wordpress.conf\na2dissite 000-default.conf\nsystemctl reload apache2\n\n# Final message\necho \"WordPress installation complete! Please visit your server'\''s IP address or domain name to finish the setup.\"\n" } ], "successHook": "", "volumes": []}'