feat: fully vibecoded configuration
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
name: Deploy Coolify to VPS
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
# Inject secrets directly into Terraform via TF_VAR_ environment variables
|
||||||
|
TF_VAR_ovh_endpoint: "ovh-eu"
|
||||||
|
TF_VAR_ovh_application_key: ${{ secrets.OVH_APPLICATION_KEY }}
|
||||||
|
TF_VAR_ovh_application_secret: ${{ secrets.OVH_APPLICATION_SECRET }}
|
||||||
|
TF_VAR_ovh_consumer_key: ${{ secrets.OVH_CONSUMER_KEY }}
|
||||||
|
TF_VAR_ovh_service_name: ${{ secrets.OVH_SERVICE_NAME }}
|
||||||
|
TF_VAR_ssh_public_key: ${{ secrets.SSH_PUBLIC_KEY }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Terraform
|
||||||
|
uses: hashicorp/setup-terraform@v2
|
||||||
|
with:
|
||||||
|
terraform_version: "1.5.0"
|
||||||
|
|
||||||
|
- name: Terraform Init
|
||||||
|
run: |
|
||||||
|
terraform init \
|
||||||
|
-backend-config="address=${{ github.server_url }}/api/v1/packages/${{ github.repository_owner }}/terraform/state/coolify" \
|
||||||
|
-backend-config="username=${{ github.actor }}" \
|
||||||
|
-backend-config="password=${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
working-directory: terraform
|
||||||
|
|
||||||
|
- name: Terraform Plan
|
||||||
|
run: terraform plan
|
||||||
|
working-directory: terraform
|
||||||
|
|
||||||
|
- name: Terraform Apply
|
||||||
|
run: terraform apply -auto-approve
|
||||||
|
working-directory: terraform
|
||||||
|
|
||||||
|
- name: Install Ansible
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y ansible
|
||||||
|
|
||||||
|
- name: Configure SSH Private Key
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
|
||||||
|
chmod 600 ~/.ssh/id_rsa
|
||||||
|
|
||||||
|
- name: Run Ansible Playbook
|
||||||
|
run: |
|
||||||
|
ansible-playbook playbook.yml --private-key ~/.ssh/id_rsa
|
||||||
|
working-directory: ansible
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
# Deploy Coolify to OVH VPS with Terraform, Ansible, and Gitea Actions
|
||||||
|
|
||||||
|
This repository contains all the configuration needed to dynamically provision a virtual private server (VPS) on OVH Cloud using **Terraform**, configure the VPS and install **Coolify** using **Ansible**, and automate the entire workflow via **Gitea Actions** on every push to the `main` or `master` branches.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ Architecture & Pipeline Flow
|
||||||
|
|
||||||
|
The deployment workflow is fully automated and consists of the following steps:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[Push to main/master] --> B[Gitea Actions Runner Starts]
|
||||||
|
B --> C[Terraform Initializes & Applies]
|
||||||
|
C -->|Creates SSH Key & Instance| D[OVH Public Cloud VPS]
|
||||||
|
C -->|Generates| E[ansible/inventory.ini]
|
||||||
|
B --> F[Ansible Environment Setup]
|
||||||
|
F -->|Reads inventory.ini| G[Ansible Playbook Run]
|
||||||
|
G -->|Configures Firewall & Installs| H[Coolify on VPS]
|
||||||
|
H --> I[Ready to Use on Port 8000/80/443]
|
||||||
|
```
|
||||||
|
|
||||||
|
1. **Gitea Actions** is triggered on a `push` to the default branch.
|
||||||
|
2. **Terraform** provisions an OVH Public Cloud instance (acting as our VPS), registers the deployment public SSH key on the instance, and outputs the public IP address.
|
||||||
|
3. **Terraform** dynamically creates the Ansible `inventory.ini` using the provisioned VPS public IP.
|
||||||
|
4. **Ansible** waits for SSH to become ready, updates system packages, configures the `ufw` firewall (opening ports 22, 80, 443, 8000, 6001), and runs the official non-interactive Coolify installation script safely and idempotently.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Repository Structure
|
||||||
|
|
||||||
|
```text
|
||||||
|
├── .gitea/
|
||||||
|
│ └── workflows/
|
||||||
|
│ └── deploy.yml # Gitea Actions CI/CD workflow configuration
|
||||||
|
├── ansible/
|
||||||
|
│ ├── ansible.cfg # Ansible master configuration
|
||||||
|
│ ├── inventory.tpl # Template file for dynamic inventory generation
|
||||||
|
│ └── playbook.yml # Playbook to configure VPS and install Coolify
|
||||||
|
├── terraform/
|
||||||
|
│ ├── main.tf # Terraform core resources (OVH instance, SSH key)
|
||||||
|
│ ├── outputs.tf # Outputs the VPS IP address and Instance ID
|
||||||
|
│ ├── providers.tf # Configures the OVH Terraform Provider
|
||||||
|
│ └── variables.tf # Input variables with sensible defaults
|
||||||
|
├── .gitignore # Prevents secrets/state files from being committed
|
||||||
|
└── README.md # This documentation file
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Prerequisites
|
||||||
|
|
||||||
|
To run this pipeline, you will need:
|
||||||
|
|
||||||
|
1. **An OVH Cloud Account** with an active **Public Cloud Project**.
|
||||||
|
2. **OVH API Credentials** with permissions to manage Public Cloud resources.
|
||||||
|
3. **A Gitea Instance** with Gitea Actions enabled and a runner registered.
|
||||||
|
|
||||||
|
### 🔑 Step 1: Generate OVH API Credentials
|
||||||
|
|
||||||
|
Go to [OVH API Keys Creation Page](https://eu.api.ovh.com/createToken/) (or the corresponding URL for your OVH region, e.g. CA or US) and create a new set of API keys. Give your token access to `/cloud/*` and `/me/*` paths.
|
||||||
|
|
||||||
|
Once created, you will receive:
|
||||||
|
- `Application Key` (AK)
|
||||||
|
- `Application Secret` (AS)
|
||||||
|
- `Consumer Key` (CK)
|
||||||
|
|
||||||
|
You also need your **Public Cloud Project ID** (often called `Service Name`). This can be found on your OVH Public Cloud Dashboard (it is a string of letters and numbers like `sb123456-ovh`).
|
||||||
|
|
||||||
|
### 🗝️ Step 2: Generate an SSH Keypair
|
||||||
|
|
||||||
|
Generate an SSH keypair that Gitea Actions will use to authenticate with the new VPS:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh-keygen -t ed25519 -f id_rsa -N "" -C "gitea-actions-coolify"
|
||||||
|
```
|
||||||
|
|
||||||
|
This generates:
|
||||||
|
- `id_rsa` (Private key, to be saved in Gitea secrets)
|
||||||
|
- `id_rsa.pub` (Public key, to be saved in Gitea secrets)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Step 3: Configure Gitea Secrets
|
||||||
|
|
||||||
|
In your Gitea repository, navigate to **Settings > Actions > Secrets** and add the following secrets:
|
||||||
|
|
||||||
|
| Secret Name | Description | Example / Format |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `OVH_APPLICATION_KEY` | Your OVH API Application Key | `xxxxxx` |
|
||||||
|
| `OVH_APPLICATION_SECRET` | Your OVH API Application Secret | `xxxxxx` |
|
||||||
|
| `OVH_CONSUMER_KEY` | Your OVH API Consumer Key | `xxxxxx` |
|
||||||
|
| `OVH_SERVICE_NAME` | Your OVH Public Cloud Project ID | `ab123456-ovh-eu-1` |
|
||||||
|
| `SSH_PUBLIC_KEY` | Content of the generated `id_rsa.pub` | `ssh-ed25519 AAAAC3...` |
|
||||||
|
| `SSH_PRIVATE_KEY` | Content of the generated `id_rsa` | `-----BEGIN OPENSSH PRIVATE KEY-----...` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💻 Running Terraform Locally
|
||||||
|
|
||||||
|
If you want to run Terraform locally to inspect, plan, or deploy from your own machine, you can do so while keeping the state synchronized with your Gitea Actions pipeline.
|
||||||
|
|
||||||
|
### Step 1: Install Terraform
|
||||||
|
Make sure you have the Terraform CLI installed (`>= 1.3.0`).
|
||||||
|
|
||||||
|
### Step 2: Configure Local Credentials
|
||||||
|
Create a file named `terraform.tfvars` inside the `terraform/` directory. *(Note: This file is already configured in `.gitignore` to prevent committing credentials).*
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
ovh_endpoint = "ovh-eu"
|
||||||
|
ovh_application_key = "your_ovh_application_key"
|
||||||
|
ovh_application_secret = "your_ovh_application_secret"
|
||||||
|
ovh_consumer_key = "your_ovh_consumer_key"
|
||||||
|
ovh_service_name = "your_ovh_public_cloud_project_id"
|
||||||
|
ssh_public_key = "your_ssh_public_key_content" # or file("~/.ssh/id_rsa.pub")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Initialize with Gitea State Backend
|
||||||
|
To keep your state synchronized with Gitea Actions, run `terraform init` locally and point it to Gitea's built-in Terraform State Registry:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd terraform
|
||||||
|
|
||||||
|
terraform init \
|
||||||
|
-backend-config="address=https://<your-gitea-domain>/api/v1/packages/<your-gitea-username>/terraform/state/coolify" \
|
||||||
|
-backend-config="username=<your-gitea-username>" \
|
||||||
|
-backend-config="password=<your-gitea-token-or-password>"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Plan and Apply
|
||||||
|
Now you can plan and apply your local changes. They will automatically read from and write to the exact same state file as Gitea Actions:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Preview changes
|
||||||
|
terraform plan
|
||||||
|
|
||||||
|
# Apply changes to provision/update the VPS
|
||||||
|
terraform apply
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Customization
|
||||||
|
|
||||||
|
You can customize the deployment by overriding the default values in `terraform/variables.tf`:
|
||||||
|
|
||||||
|
- **OVH Endpoint (`ovh_endpoint`)**: Defaults to `ovh-eu`. Change to `ovh-ca` or `ovh-us` depending on your account region.
|
||||||
|
- **Region (`region`)**: Defaults to `GRA11` (Gravelines, France). Other popular regions: `SBG5` (Strasbourg), `WAW1` (Warsaw), `DE1` (Frankfurt), `UK1` (London).
|
||||||
|
- **Instance Size/Flavor (`flavor_name`)**: Defaults to `b2-7` (2 vCPUs, 7 GB RAM). If you want a smaller/starter setup, you can use `s1-2` (1 vCPU, 2 GB RAM) or `d2-4` (2 vCPUs, 4 GB RAM). Coolify operates best with at least 2 vCPUs and 4 GB RAM.
|
||||||
|
- **Operating System (`image_name`)**: Defaults to `Ubuntu 22.04`. Coolify supports Ubuntu 22.04/24.04 and Debian 11/12 out of the box.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Post-Deployment: Accessing Coolify
|
||||||
|
|
||||||
|
Once the Gitea Actions pipeline completes successfully:
|
||||||
|
|
||||||
|
1. Copy the public IP address of the server from the Terraform output or the Gitea Actions log.
|
||||||
|
2. Open your browser and navigate to `http://<your-vps-ip>:8000`.
|
||||||
|
3. Register your administrator account.
|
||||||
|
4. Set up your wild-card domain or custom domains inside Coolify!
|
||||||
|
|
||||||
|
Enjoy your brand-new, self-hosted, enterprise-grade PaaS on OVH VPS!
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
[defaults]
|
||||||
|
inventory = inventory.ini
|
||||||
|
host_key_checking = False
|
||||||
|
retry_files_enabled = False
|
||||||
|
stdout_callback = yaml
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[coolify_vps]
|
||||||
|
${vps_ip} ansible_user=ubuntu
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
- name: Configure VPS and Install Coolify
|
||||||
|
hosts: coolify_vps
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Wait for VPS to become reachable via SSH
|
||||||
|
ansible.builtin.wait_for_connection:
|
||||||
|
delay: 5
|
||||||
|
timeout: 300
|
||||||
|
|
||||||
|
- name: Update apt package cache
|
||||||
|
ansible.builtin.apt:
|
||||||
|
update_cache: yes
|
||||||
|
cache_valid_time: 3600
|
||||||
|
|
||||||
|
- name: Install system prerequisites
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- curl
|
||||||
|
- git
|
||||||
|
- ufw
|
||||||
|
- jq
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Configure UFW firewall
|
||||||
|
block:
|
||||||
|
- name: Allow SSH traffic
|
||||||
|
ansible.builtin.shell: ufw allow 22/tcp
|
||||||
|
- name: Allow HTTP traffic
|
||||||
|
ansible.builtin.shell: ufw allow 80/tcp
|
||||||
|
- name: Allow HTTPS traffic
|
||||||
|
ansible.builtin.shell: ufw allow 443/tcp
|
||||||
|
- name: Allow Coolify Console traffic
|
||||||
|
ansible.builtin.shell: ufw allow 8000/tcp
|
||||||
|
- name: Allow Realtime/Websocket traffic
|
||||||
|
ansible.builtin.shell: ufw allow 6001/tcp
|
||||||
|
- name: Enable UFW firewall
|
||||||
|
ansible.builtin.shell: echo "y" | ufw enable
|
||||||
|
rescue:
|
||||||
|
- name: Log firewall configuration failure
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "Failed to set up firewall, continuing with installation."
|
||||||
|
|
||||||
|
- name: Check if Coolify is already installed
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: /data/coolify/source/.env
|
||||||
|
register: coolify_installed
|
||||||
|
|
||||||
|
- name: Install Coolify via official installation script
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
|
||||||
|
register: install_output
|
||||||
|
when: not coolify_installed.stat.exists
|
||||||
|
async: 600
|
||||||
|
poll: 10
|
||||||
|
|
||||||
|
- name: Print installation output
|
||||||
|
ansible.builtin.debug:
|
||||||
|
var: install_output.stdout_lines
|
||||||
|
when: install_output.changed
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
resource "ovh_cloud_project_keypair" "deploy_key" {
|
||||||
|
service_name = var.ovh_service_name
|
||||||
|
name = "coolify_deploy_key"
|
||||||
|
public_key = var.ssh_public_key
|
||||||
|
region = var.region
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "ovh_cloud_project_instance" "coolify" {
|
||||||
|
service_name = var.ovh_service_name
|
||||||
|
name = var.instance_name
|
||||||
|
region = var.region
|
||||||
|
flavor_name = var.flavor_name
|
||||||
|
image_name = var.image_name
|
||||||
|
key_pair = ovh_cloud_project_keypair.deploy_key.name
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate inventory file for Ansible
|
||||||
|
resource "local_file" "ansible_inventory" {
|
||||||
|
content = templatefile("${path.module}/../ansible/inventory.tpl", {
|
||||||
|
vps_ip = ovh_cloud_project_instance.coolify.ipv4_addresses[0]
|
||||||
|
})
|
||||||
|
filename = "${path.module}/../ansible/inventory.ini"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
output "vps_ip" {
|
||||||
|
description = "The public IPv4 address of the Coolify VPS"
|
||||||
|
value = ovh_cloud_project_instance.coolify.ipv4_addresses[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "instance_id" {
|
||||||
|
description = "The ID of the provisioned OVH instance"
|
||||||
|
value = ovh_cloud_project_instance.coolify.id
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
terraform {
|
||||||
|
required_version = ">= 1.3.0"
|
||||||
|
required_providers {
|
||||||
|
ovh = {
|
||||||
|
source = "ovh/ovh"
|
||||||
|
version = "~> 0.43.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
backend "http" {} # Allows local and Gitea Actions to share state securely
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "ovh" {
|
||||||
|
endpoint = var.ovh_endpoint
|
||||||
|
application_key = var.ovh_application_key
|
||||||
|
application_secret = var.ovh_application_secret
|
||||||
|
consumer_key = var.ovh_consumer_key
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
variable "ovh_endpoint" {
|
||||||
|
type = string
|
||||||
|
description = "OVH API endpoint (e.g., ovh-eu, ovh-us, ovh-ca)"
|
||||||
|
default = "ovh-eu"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ovh_application_key" {
|
||||||
|
type = string
|
||||||
|
description = "OVH Application Key"
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ovh_application_secret" {
|
||||||
|
type = string
|
||||||
|
description = "OVH Application Secret"
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ovh_consumer_key" {
|
||||||
|
type = string
|
||||||
|
description = "OVH Consumer Key"
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ovh_service_name" {
|
||||||
|
type = string
|
||||||
|
description = "OVH Public Cloud Project ID (Service Name)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "region" {
|
||||||
|
type = string
|
||||||
|
description = "OVH Region to deploy the VPS in (e.g., GRA11, SBG5, WAW1)"
|
||||||
|
default = "GRA11"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "instance_name" {
|
||||||
|
type = string
|
||||||
|
description = "Name of the VPS/Instance"
|
||||||
|
default = "coolify-vps"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "flavor_name" {
|
||||||
|
type = string
|
||||||
|
description = "The hardware flavor / size of the VPS (Coolify recommends at least 2 vCPUs and 4GB RAM)"
|
||||||
|
default = "b2-7" # 2 vCPUs, 7GB RAM (excellent for Coolify)
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "image_name" {
|
||||||
|
type = string
|
||||||
|
description = "The OS Image name"
|
||||||
|
default = "Ubuntu 22.04"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_public_key" {
|
||||||
|
type = string
|
||||||
|
description = "SSH public key content to be authorized on the VPS"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user