initial commit
This commit is contained in:
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal 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
43
README.md
Normal 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
15
ansible/inventory.yml
Normal 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
204
ansible/playbook.yml
Normal 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
24
terraform/.terraform.lock.hcl
generated
Normal 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
26
terraform/main.tf
Normal 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
7
terraform/outputs.tf
Normal 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
14
terraform/provider.tf
Normal 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
5
terraform/variables.tf
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
variable "hcloud_token" {
|
||||||
|
description = "Hetzner Cloud API Token"
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user