commit ab3009d3294c8dfb984142275aecb074a9bf0ead Author: Evan Richardson Date: Thu Aug 28 22:54:52 2025 -0700 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d5cbd9e --- /dev/null +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1ee00dc --- /dev/null +++ b/README.md @@ -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 +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" \ No newline at end of file diff --git a/ansible/inventory.yml b/ansible/inventory.yml new file mode 100644 index 0000000..abf9dc9 --- /dev/null +++ b/ansible/inventory.yml @@ -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 \ No newline at end of file diff --git a/ansible/playbook.yml b/ansible/playbook.yml new file mode 100644 index 0000000..6155684 --- /dev/null +++ b/ansible/playbook.yml @@ -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 \ No newline at end of file diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl new file mode 100644 index 0000000..1c69e9f --- /dev/null +++ b/terraform/.terraform.lock.hcl @@ -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", + ] +} diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..509e30b --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,26 @@ +# Upload SSH key to Hetzner +resource "hcloud_ssh_key" "hetzner" { + name = "Hetzner-Key" + public_key = <