initial commit

This commit is contained in:
2025-08-28 22:54:52 -07:00
commit ab3009d329
9 changed files with 368 additions and 0 deletions

30
.gitignore vendored Normal file
View File

@@ -0,0 +1,30 @@
# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.log
crash.*.log
# Exclude all .tfvars files, which are likely to contain sensitive data
*.tfvars
*.tfvars.json
# Ignore override files as they are usually used to override resources locally
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Include override files you do wish to add to version control using negated pattern
# !example_override.tf
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
*tfplan*
# Ignore CLI configuration files
.terraformrc
terraform.rc

43
README.md Normal file
View File

@@ -0,0 +1,43 @@
# Terraform Hetzner Setup
This Terraform configuration creates a single Hetzner Cloud server in Nuremberg and manages an SSH key. It also outputs the IPv4 and IPv6 addresses of the server.
> ⚠️ Note: Hetzner Cloud Terraform provider does **not** support object storage buckets natively. You will need to create any buckets manually or use an S3-compatible provider.
---
## Project Structure
terraform-hetzner/
├── main.tf # SSH key, server resource
├── provider.tf # Hetzner provider configuration
├── variables.tf # Input variables (API token)
├── outputs.tf # Outputs IPv4 and IPv6 addresses
---
## Prerequisites
- [Terraform](https://www.terraform.io/downloads) >= 1.3
- Hetzner Cloud account
- Hetzner Cloud API token
- Your SSH public key
---
## Steps to Run
1. **Clone this repository** or copy the files to a folder:
```bash
git clone <your-repo-url>
cd terraform
2. Set your Hetzner API token in the environment
export HCLOUD_TOKEN="your-api-token"
4. Initialize Terraform
terraform init
terraform apply -var="hcloud_token=$HCLOUD_TOKEN"

15
ansible/inventory.yml Normal file
View File

@@ -0,0 +1,15 @@
# inventory.yml
# Replace YOUR_SERVER_IP with the actual IP address of your Hetzner server
all:
children:
supabase_servers:
hosts:
supabase-1:
ansible_host: 91.99.108.216
ansible_user: root # or ubuntu/debian depending on your image
ansible_ssh_private_key_file: ~/.ssh/your_private_key # path to your SSH key
# Alternative format if you prefer:
# [supabase_servers]
# supabase-1 ansible_host=YOUR_SERVER_IP ansible_user=root

204
ansible/playbook.yml Normal file
View File

@@ -0,0 +1,204 @@
---
- name: Setup Supabase, PowerSync, and Docker on Hetzner Server
hosts: supabase_servers
become: true
gather_facts: true
vars:
supabase_dir: /opt/supabase
powersync_dir: /opt/powersync
docker_compose_version: "2.21.0"
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required system packages
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
- software-properties-common
- git
- wget
- unzip
- htop
- vim
- ufw
state: present
- name: Add Docker's official GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Add Docker repository
apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
- name: Install Docker CE
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
update_cache: yes
- name: Start and enable Docker service
systemd:
name: docker
state: started
enabled: yes
- name: Add current user to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
- name: Create /opt directory if it doesn't exist
file:
path: /opt
state: directory
mode: '0755'
- name: Clone Supabase repository
git:
repo: https://github.com/supabase/supabase
dest: "{{ supabase_dir }}"
depth: 1
force: yes
- name: Set proper ownership for Supabase directory
file:
path: "{{ supabase_dir }}"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
recurse: yes
- name: Install Node.js 18.x repository
shell: curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
args:
creates: /etc/apt/sources.list.d/nodesource.list
- name: Install Node.js
apt:
name: nodejs
state: present
update_cache: yes
- name: Install Supabase CLI
npm:
name: supabase
global: yes
state: present
- name: Create PowerSync directory
file:
path: "{{ powersync_dir }}"
state: directory
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0755'
- name: Download PowerSync Server
get_url:
url: https://github.com/powersync-ja/powersync-service/releases/latest/download/powersync-server-linux-amd64.tar.gz
dest: /tmp/powersync-server.tar.gz
mode: '0644'
- name: Extract PowerSync Server
unarchive:
src: /tmp/powersync-server.tar.gz
dest: "{{ powersync_dir }}"
remote_src: yes
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
- name: Copy Supabase docker-compose.yml to working directory
copy:
src: "{{ supabase_dir }}/docker/docker-compose.yml"
dest: "{{ supabase_dir }}/docker-compose.yml"
remote_src: yes
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
- name: Copy Supabase .env.example to .env
copy:
src: "{{ supabase_dir }}/docker/.env.example"
dest: "{{ supabase_dir }}/.env"
remote_src: yes
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
force: no
- name: Configure UFW firewall
ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- '22' # SSH
- '80' # HTTP
- '443' # HTTPS
- '3000' # Supabase Studio
- '8000' # Supabase API
- '5432' # PostgreSQL
- '8080' # PowerSync
- name: Enable UFW
ufw:
state: enabled
- name: Create systemd service for Supabase
template:
src: supabase.service.j2
dest: /etc/systemd/system/supabase.service
mode: '0644'
notify: restart supabase
- name: Create systemd service for PowerSync
template:
src: powersync.service.j2
dest: /etc/systemd/system/powersync.service
mode: '0644'
notify: restart powersync
- name: Reload systemd daemon
systemd:
daemon_reload: yes
- name: Start and enable Supabase service
systemd:
name: supabase
state: started
enabled: yes
- name: Display setup information
debug:
msg:
- "Supabase has been installed in {{ supabase_dir }}"
- "PowerSync has been installed in {{ powersync_dir }}"
- "Supabase Studio will be available at http://{{ ansible_default_ipv4.address }}:3000"
- "Supabase API will be available at http://{{ ansible_default_ipv4.address }}:8000"
- "To start Supabase: cd {{ supabase_dir }} && docker compose up -d"
- "Configuration file: {{ supabase_dir }}/.env"
handlers:
- name: restart supabase
systemd:
name: supabase
state: restarted
- name: restart powersync
systemd:
name: powersync
state: restarted

24
terraform/.terraform.lock.hcl generated Normal file
View File

@@ -0,0 +1,24 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hetznercloud/hcloud" {
version = "1.52.0"
constraints = "~> 1.47"
hashes = [
"h1:bDV74LEXxQWmpQ9OsxNPoXfhHwaWIpiS9oS+7g8gVPw=",
"zh:1e9bb6b6a2ea5f441638dbae2d60fbe04ff455f58a18c740b8b7913e2197d875",
"zh:29c122e404ba331cfbadacc7f1294de5a31c9dfd60bdfe3e1b402271fc8e419c",
"zh:2bd0ae2f0bb9f16b7753f59a08e57ac7230f9c471278d7882f81406b9426c8c7",
"zh:4383206971873f6b5d81580a9a36e0158924f5816ebb6206b0cf2430e4e6a609",
"zh:47e2ca1cfa18500e4952ab51dc357a0450d00a92da9ea03e452f1f3efe6bbf75",
"zh:8e9fe90e3cea29bb7892b64da737642fc22b0106402df76c228a3cbe99663278",
"zh:a2d69350a69c471ddb63bcc74e105e585319a0fc0f4d1b7f70569f6d2ece5824",
"zh:a97abcc254e21c294e2d6b0fc9068acfd63614b097dda365f1c56ea8b0fd5f6b",
"zh:aba8d72d4fe2e89c922d5446d329e5c23d00b28227b4666e6486ba18ea2ec278",
"zh:ad36c333978c2d9e4bc43dcadcbff42fe771a8c5ef53d028bcacec8287bf78a7",
"zh:cdb1e6903b9d2f0ad8845d4eb390fbe724ee2435fb045baeab38d4319e637682",
"zh:df77b08757f3f36b8aadb33d73362320174047044414325c56a87983f48b5186",
"zh:e07513d5ad387247092b5ae1c87e21a387fc51873b3f38eee616187e38b090a7",
"zh:e2be02bdc59343ff4b9e26c3b93db7680aaf3e6ed13c8c4c4b144c74c2689915",
]
}

26
terraform/main.tf Normal file
View File

@@ -0,0 +1,26 @@
# Upload SSH key to Hetzner
resource "hcloud_ssh_key" "hetzner" {
name = "Hetzner-Key"
public_key = <<EOT
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOqDZVn9hROA7du2xT43CBZe2rAedqH2hFVAV5YDIOYa
EOT
}
resource "hcloud_server" "supabase_1" {
name = "supabase-1"
server_type = "cax21"
image = "ubuntu-24.04"
location = "nbg1" # Nuremberg
ssh_keys = [hcloud_ssh_key.hetzner.name]
public_net {
ipv4_enabled = true
ipv6_enabled = true
}
}
# # Object Storage bucket (Spaces-compatible)
# resource "hcloud_object_storage" "user_files" {
# name = "user-files"
# location = "nbg1" # Nuremberg
# }

7
terraform/outputs.tf Normal file
View File

@@ -0,0 +1,7 @@
output "supabase_1_ipv4" {
value = hcloud_server.supabase_1.ipv4_address
}
output "supabase_1_ipv6" {
value = hcloud_server.supabase_1.ipv6_address
}

14
terraform/provider.tf Normal file
View File

@@ -0,0 +1,14 @@
terraform {
required_providers {
hcloud = {
source = "hetznercloud/hcloud"
version = "~> 1.47"
}
}
required_version = ">= 1.3.0"
}
provider "hcloud" {
token = var.hcloud_token
}

5
terraform/variables.tf Normal file
View File

@@ -0,0 +1,5 @@
variable "hcloud_token" {
description = "Hetzner Cloud API Token"
type = string
sensitive = true
}