Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
ed9bc45
Test IAC
jonakoudijs Sep 21, 2025
029daf9
Fix terraform aactions path
jonakoudijs Sep 21, 2025
0c471a1
Fix Terraform Lint
jonakoudijs Sep 21, 2025
3e07923
Rename GH action
jonakoudijs Sep 21, 2025
982dec6
Test secrets
jonakoudijs Sep 21, 2025
1a481bf
Test secrets
jonakoudijs Sep 21, 2025
2ba607b
Test secrets
jonakoudijs Sep 21, 2025
13e0552
Test secrets
jonakoudijs Sep 21, 2025
58d6f85
Test secrets
jonakoudijs Sep 22, 2025
7ea3035
Test secrets
jonakoudijs Sep 22, 2025
1ff2456
Test
jonakoudijs Sep 22, 2025
ab0cbcc
Add first IAC setup
jonakoudijs Mar 10, 2026
da500ad
Fix incorrect path in GH Actions
jonakoudijs Mar 10, 2026
749c585
Reformat TF code to match fmt cmd
jonakoudijs Mar 10, 2026
472011c
Fix incorrect path in GH Actions
jonakoudijs Mar 10, 2026
8d302c4
Test backend config
jonakoudijs Mar 10, 2026
1ec1a53
Test backend config
jonakoudijs Mar 10, 2026
af587b4
Test backend config
jonakoudijs Mar 10, 2026
8827171
Test backend config
jonakoudijs Mar 10, 2026
d410fe8
Test backend config
jonakoudijs Mar 10, 2026
4806e73
Test backend config
jonakoudijs Mar 10, 2026
a1bd83c
Test backend config
jonakoudijs Mar 10, 2026
4ff8e7a
Test backend config
jonakoudijs Mar 10, 2026
2aa70ea
Test backend config
jonakoudijs Mar 10, 2026
5a78008
Test backend config
jonakoudijs Mar 10, 2026
958e54a
Test backend config
jonakoudijs Mar 10, 2026
402fa41
Test backend config
jonakoudijs Mar 10, 2026
f4af7e0
Test backend config
jonakoudijs Mar 10, 2026
f2a7f20
Test backend config
jonakoudijs Mar 10, 2026
87358a4
Test backend config
jonakoudijs Mar 10, 2026
4157ab4
Test backend config
jonakoudijs Mar 10, 2026
468ca53
Test backend config
jonakoudijs Mar 10, 2026
4d5824f
Test backend config
jonakoudijs Mar 10, 2026
1fb0839
Test backend config
jonakoudijs Mar 10, 2026
5399be6
Test backend config
jonakoudijs Mar 10, 2026
fce291d
Test backend config
jonakoudijs Mar 10, 2026
fd01c45
Test backend config
jonakoudijs Mar 10, 2026
4d09608
Test backend config
jonakoudijs Mar 10, 2026
28c7e32
Test backend config
jonakoudijs Mar 10, 2026
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
51 changes: 51 additions & 0 deletions .github/workflows/apply.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Apply Terraform plan

on:
push:
branches:
- main

permissions:
contents: read
pull-requests: write

jobs:
apply:
runs-on: ubuntu-latest
name: Apply Terraform plan
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TF_VAR_hcloud_token: ${{ secrets.TF_HCLOUD_TOKEN }}
STATE_BUCKET_NAME: ${{ secrets.TF_STATE_BUCKET_NAME }}
STATE_BUCKET_KEY: ${{ secrets.TF_STATE_BUCKET_KEY }}
AWS_ACCESS_KEY_ID: ${{ secrets.TF_STATE_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TF_STATE_SECRET_KEY }}
AWS_CA_BUNDLE: ""
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Homebrew
run: |
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo >> ~/.bashrc
echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv bash)"' >> ~/.bashrc
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv bash)"
env:
NONINTERACTIVE: 1

- name: Install Packer
run: |
sudo apt update
sudo apt install packer
packer
locate packer

- name: Install Talosctl
run: /home/linuxbrew/.linuxbrew/bin/brew install siderolabs/tap/talosctl

- name: Terraform apply
uses: dflook/terraform-apply@v2
with:
path: infra
backend_config: bucket=${{ env.STATE_BUCKET_NAME }} key=${{ env.STATE_BUCKET_KEY }}
31 changes: 31 additions & 0 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Lint Terraform plan

on:
push:
branches-ignore:
- main

jobs:
validate:
runs-on: ubuntu-latest
name: Validate Terraform
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Terraform validate
uses: dflook/terraform-validate@v2
with:
path: infra

fmt-check:
runs-on: ubuntu-latest
name: Terraform formatting
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Terraform fmt
uses: dflook/terraform-fmt-check@v2
with:
path: infra
61 changes: 61 additions & 0 deletions .github/workflows/plan.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Create Terraform plan

on: [pull_request]

permissions:
contents: read
pull-requests: write

jobs:
plan:
runs-on: ubuntu-latest
name: Create a Terraform plan
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TF_VAR_hcloud_token: ${{ secrets.TF_HCLOUD_TOKEN }}
STATE_BUCKET_NAME: ${{ secrets.TF_STATE_BUCKET_NAME }}
STATE_BUCKET_KEY: ${{ secrets.TF_STATE_BUCKET_KEY }}
AWS_ACCESS_KEY_ID: ${{ secrets.TF_STATE_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TF_STATE_SECRET_KEY }}
AWS_CA_BUNDLE: ""
steps:
- name: Checkout
uses: actions/checkout@v4

# - name: Setup Homebrew
# run: |
# /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# echo >> ~/.bashrc
# echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv bash)"' >> ~/.bashrc
# eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv bash)"
# env:
# NONINTERACTIVE: 1

# - name: Install Packer
# run: |
# sudo wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
# sudo echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(grep -oP '(?<=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
# sudo apt update
# sudo apt install packer

# - name: Install Talosctl
# run: curl -sL https://talos.dev/install | sh

- name: Terraform plan
uses: dflook/terraform-plan@v2
with:
path: infra
backend_config: bucket=${{ env.STATE_BUCKET_NAME }} key=${{ env.STATE_BUCKET_KEY }}
env:
TERRAFORM_PRE_RUN: |
# Install prerequisites
apt update
apt install -y lsb-release

# Install latest Packer
wget -O - https://apt.releases.hashicorp.com/gpg | gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(grep -oP '(?<=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main" | tee /etc/apt/sources.list.d/hashicorp.list
apt install -y packer

# Install latest Talosctl
curl -sL https://talos.dev/install | sh
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@ terraform.tfvars
*.auto.tfvars

# secrets
kubeconfig
talosconfig
.kubeconfig
.kubeconfig.bak
.talosconfig
.talosconfig.bak
.env
.environment
41 changes: 34 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,42 @@

# Cloudijs Platform

Hosting platform based on Kubernetes. This repository contain the IAC used to setup this platform on [Hetzner](https://www.hetzner.com). The platform relies heavily on the amazing [terraform-hcloud-kubernetes](https://github.com/hcloud-k8s/terraform-hcloud-kubernetes) Terraform module.
Hosting platform based on Kubernetes. This repository contain the Terraform code used to setup this platform on [Hetzner](https://www.hetzner.com). The platform relies heavily on the amazing [terraform-hcloud-kubernetes](https://github.com/hcloud-k8s/terraform-hcloud-kubernetes) Tofu/Terraform module.

## Pre-requisites

* [Hetzner Account](https://www.hetzner.com)
* [OpenTofu](https://opentofu.org/docs/intro/install/)
* [Taskfile](https://taskfile.dev/docs/installation)

## Deployment

To deploy the platform you will need a Hetzer account and create a [token](https://docs.hetzner.com/cloud/api/getting-started/generating-api-token/). Then run Terraform or Tofu after setting the token variable:
```bash
export TF_VAR_hcloud_token="<your-token>"
tofu plan
tofy apply
To deploy the platform you will need a Hetzner account, create an S3 bucket and create an [API token](https://docs.hetzner.com/cloud/api/getting-started/generating-api-token/). Make sure to gather the values for the S3 bucket configuration. These can be specified as environment variables or in an `.env` file:
```sh
export HCLOUD_TOKEN="<your-token>"

export STATE_BUCKET_NAME="<your-object-storage-bucket-name>"
export STATE_BUCKET_KEY="<your-object-storage-bucket-key>"

export STATE_BUCKET_ACCESS_KEY="<your-s3-credentials-access-key>"
export STATE_BUCKET_SECRET_KEY="<your-s3-credentials-secret-key>"
```
```
# .env

HCLOUD_TOKEN="<your-token>"

STATE_BUCKET_NAME="<your-object-storage-bucket-name>"
STATE_BUCKET_KEY="<your-object-storage-bucket-key>"

STATE_BUCKET_ACCESS_KEY="<your-s3-credentials-access-key>"
STATE_BUCKET_SECRET_KEY="<your-s3-credentials-secret-key>"
```
Run Tofu after setting the required variables to setup the platform:
```sh
task create
```
Other available tasks like destroying the environment can be found using the `task` command.

## Sources

Expand All @@ -20,7 +46,8 @@ tofy apply
* https://registry.terraform.io/providers/hetznercloud/hcloud/latest
* https://docs.hetzner.cloud/changelog#2025-04-23-talos-linux-v195-iso-now-available
* https://github.com/hetznercloud/hcloud-cloud-controller-manager/tree/main
* https://github.com/dflook/terraform-github-actions

## License

[MIT license](LICENSE)
[MIT license](LICENSE)
57 changes: 57 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
version: "3"

dotenv:
- .env
- ../.env

tasks:
default:
desc: List available tasks
cmds:
- task --list-all

create:
desc: Create all resources
dir: infra/
cmds:
- tofu init -backend-config="bucket=${STATE_BUCKET_NAME}" -backend-config="key=${STATE_BUCKET_KEY}" -upgrade
- tofu apply -auto-approve
env:
AWS_ACCESS_KEY_ID:
sh: echo "${STATE_BUCKET_ACCESS_KEY}"
AWS_SECRET_ACCESS_KEY:
sh: echo "${STATE_BUCKET_SECRET_KEY}"
TF_VAR_hcloud_token:
sh: echo "${HCLOUD_TOKEN}"

delete:
desc: Destroy all resources
dir: infra/
cmds:
- cmd: tofu state rm 'module.kubernetes.talos_machine_configuration_apply.worker'
ignore_error: true
- cmd: tofu state rm 'module.kubernetes.talos_machine_configuration_apply.control_plane'
ignore_error: true
- cmd: tofu state rm 'module.kubernetes.talos_machine_secrets.this'
ignore_error: true
- cmd: tofu destroy -auto-approve
- cmd: rm -rf terraform terraform.lock.hcl ../.kubeconfig ../.kubeconfig.bak ../.talosconfig ../.talosconfig.bak
env:
AWS_ACCESS_KEY_ID:
sh: echo "${STATE_BUCKET_ACCESS_KEY}"
AWS_SECRET_ACCESS_KEY:
sh: echo "${STATE_BUCKET_SECRET_KEY}"
TF_VAR_hcloud_token:
sh: echo "${HCLOUD_TOKEN}"

validate:
desc: Run syntax and linting checks
cmds:
- cmd: tflint --chdir infra/

recreate:
desc: Recreate all resources
cmds:
- task: delete
- task: create
11 changes: 11 additions & 0 deletions infra/backend.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
terraform {
backend "s3" {
region = "us-east-1" # Required but not used by Hetzner
endpoints = { s3 = "https://fsn1.your-objectstorage.com" } # Falkenstein region
use_path_style = true
skip_credentials_validation = true
skip_region_validation = true
skip_requesting_account_id = true
skip_metadata_api_check = true
}
}
39 changes: 39 additions & 0 deletions infra/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module "kubernetes" {
source = "hcloud-k8s/kubernetes/hcloud"
version = "3.26.1"

# General configuration
cluster_name = "platform"
hcloud_token = var.hcloud_token

cluster_delete_protection = false

# Export configs for talosctl and kubectl
cluster_kubeconfig_path = var.kubernetes_config_path
cluster_talosconfig_path = var.talos_config_path

# Firewall configuration
firewall_use_current_ipv4 = true

# Enable Cilium Gateway API and Cert Manager
cert_manager_enabled = true
cilium_gateway_api_enabled = true

control_plane_nodepools = [
{ name = "control", type = "cx23", location = "fsn1", count = 1 }
]
worker_nodepools = [
{ name = "worker", type = "cx33", location = "fsn1", count = 2 }
]
}

# Setup Cloudijs System
module "cloudijs-system" {
depends_on = [module.kubernetes]
source = "./modules/cloudijs-system"

namespace = var.system_namespace
repository_url = var.repository_url
repository_ref = var.repository_ref
repository_path = var.repository_path
}
13 changes: 13 additions & 0 deletions infra/manifests/issuer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: http-issuer
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: http-issuer-account-key
solvers:
- http01:
ingress:
ingressClassName: nginx
44 changes: 44 additions & 0 deletions infra/modules/cloudijs-system/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Create namespace for Cloudijs system deployments
resource "kubernetes_namespace_v1" "platform_system" {
metadata {
name = var.namespace
}
}

# Deploy and configure Flux Operator
resource "helm_release" "flux_operator" {
depends_on = [kubernetes_namespace_v1.platform_system]

name = "flux-operator"
namespace = var.namespace
repository = "oci://ghcr.io/controlplaneio-fluxcd/charts"
chart = "flux-operator"
}

resource "helm_release" "flux_instance" {
depends_on = [helm_release.flux_operator]

name = "flux"
namespace = var.namespace
repository = "oci://ghcr.io/controlplaneio-fluxcd/charts"
chart = "flux-instance"

set = [
{
name = "instance.sync.kind"
value = "GitRepository"
},
{
name = "instance.sync.url"
value = var.repository_url
},
{
name = "instance.sync.ref"
value = var.repository_ref
},
{
name = "instance.sync.path"
value = var.repository_path
}
]
}
Loading
Loading