Add(module): add girasol module

Add(module): add amap module
Add(module): add common lib and services
Add(module): add base structure for keycloak
Add(module): add base structure for rocket
Add(module): add n8n and windmill modules
Add(docker): add install docker script in common module
Add(template): add root for aldon.fr and mathieu.wiki in traefik.service template
This commit is contained in:
2026-04-21 16:52:41 +02:00
parent 905cc8b43d
commit a56911b896
65 changed files with 1893 additions and 23 deletions

76
main.tf
View File

@@ -24,6 +24,7 @@ locals {
gitea = module.gitea.traefik_service
fefan = module.fefan.traefik_service
listmonk = module.listmonk.traefik_service
amap = module.amap.traefik_service
}
}
@@ -177,16 +178,87 @@ module "listmonk" {
proxmox_host_ip = var.proxmox_host_ip
}
module "amap" {
source = "./modules/apps/amap"
providers = {}
vm_ip_address = "192.168.1.96"
depends_on = [
module.gitea
]
name = "amap"
hostname = "amap"
domain = "aldon.fr"
vm_id = 217
node_name = "mop"
template_id = 103
cores = 1
memory = 1024
balloon = 1024
disk_size = 16
ssh_public_key = var.ssh_public_key
proxmox_host_ip = var.proxmox_host_ip
}
# module "n8n" {
# source = "./modules/apps/n8n"
# providers = {}
# vm_ip_address = "192.168.1.94"
# depends_on = []
# name = "n8n"
# hostname = "n8n"
# domain = "mathieu.wiki"
# vm_id = 215
# node_name = "mop"
# template_id = 103
# cores = 1
# memory = 1024
# balloon = 512
# disk_size = 16
# ssh_public_key = var.ssh_public_key
# proxmox_host_ip = var.proxmox_host_ip
# }
# module "windmill" {
# source = "./modules/apps/windmill"
# providers = {}
# vm_ip_address = "192.168.1.94"
# depends_on = []
# name = "windmill"
# hostname = "windmill"
# domain = "aldon.fr"
# vm_id = 215
# node_name = "mop"
# template_id = 103
# cores = 1
# memory = 2048
# balloon = 1024
# disk_size = 16
# ssh_public_key = var.ssh_public_key
# proxmox_host_ip = var.proxmox_host_ip
# }
# module "keycloak" {
# source = "./modules/apps/keycloak"
# providers = {}
# vm_ip_address = "192.168.1.94"
# vm_ip_address = "192.168.1.95"
# depends_on = []
# name = "keycloak"
# hostname = "keycloak"
# domain = "aldon.fr"
# vm_id = 215
# vm_id = 216
# node_name = "mop"
# template_id = 103

View File

@@ -0,0 +1,36 @@
# Environment files
ENV_FILE_LOCATION=/opt/environment/.env
# gitea act_runner
ACT_RUNNER_VERSION=0.2.13
ACT_RUNNER_LOCATION=/usr/local/bin
ACT_RUNNER_USER=act_runner
GITEA_RUNNER_REGISTRATION_TOKEN=<gitea-repository-runner-token>
USERNAME=amap
# gitea instance
GITEA_INSTANCE_URL=https://gitea.aldon.fr
GITEA_SERVICE_APPLICATION_TOKEN=<gitea-auth-token>
GITEA_SERVICE_REPOSITORY=mop/amap
GITEA_WORKFLOW_NAME=deploy.yaml
# Applicaiton specifics
DB_USER=postgres
DB_PASS=
DB_NAME=amap
DB_HOST=localhost
ORIGINS=https://amap.aldon.fr
SECRET_KEY=
VITE_API_URL=https://amap.aldon.fr/api
KEYCLOAK_SERVER=https://keycloak.aldon.fr
KEYCLOAK_REALM=aldon.fr
KEYCLOAK_CLIENT_ID=
KEYCLOAK_CLIENT_SECRET=
KEYCLOAK_REDIRECT_URI=https://amap.aldon.fr/api/auth/callback
MAX_AGE=3600
DEBUG=True
# Backup specifics
SERVICE_BACKUPS_DIR=/backups/amap
SERVICE_BACKUPS_PREFIX=amap-dump
SERVICE_BACKUPS_EXTENSION=sql

View File

@@ -0,0 +1,90 @@
#cloud-config
hostname: ${hostname}
local-hostname: ${hostname}
fqdn: ${hostname}.${domain}
manage_etc_hosts: true
users:
- default
- name: ${hostname}
groups: sudo
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ${ssh_key}
disable_root: true
package_update: true
package_upgrade: false
packages:
- git
- nfs-common
- curl
- jq
- postgresql-client
mounts:
- [ "192.168.1.12:/main/backups", "/backups", "nfs", "defaults,_netdev,x-systemd.requires=network-online.target", "0", "0" ]
write_files:
- path: /opt/environment/.env
permissions: "0644"
content: |
${env-file-content}
- path: /opt/${hostname}/install-docker.sh
permissions: "0755"
content: |
${install-docker-script}
- path: /usr/local/bin/restore-backup.sh
permissions: "0755"
content: |
${restore-backup-script}
- path: /etc/systemd/system/restore-backup.service
permissions: "0644"
content: |
${restore-backup-service}
- path: /usr/local/bin/backup.sh
permissions: "0755"
content: |
${create-backup-script}
- path: /etc/systemd/system/create-backup.timer
permissions: "0644"
content: |
${create-backup-timer}
- path: /etc/systemd/system/create-backup.service
permissions: "0644"
content: |
${create-backup-service}
- path: /etc/systemd/system/act_runner.service
permissions: "0644"
content: |
${act_runner-service}
- path: /opt/${hostname}/install-runner.sh
permissions: "0755"
content: |
${act_runner-install-script}
- path: /opt/${hostname}/install-service.sh
permissions: "0755"
content: |
${service-install-script}
runcmd:
# Backup setup
- mkdir -p /backups
- mount -t nfs ${proxmox_host_ip}:/main/backups /backups
- systemctl enable --now create-backup.timer
# Docker setup
- /opt/${hostname}/install-docker.sh
# Act_runner install
- /opt/${hostname}/install-runner.sh
- systemctl daemon-reload
- systemctl enable act_runner.service
- systemctl start act_runner.service
# ${hostname} install
- /opt/${hostname}/install-service.sh
final_message: |
Base system ready for ${hostname}

View File

@@ -0,0 +1,10 @@
#!/bin/bash
set -euo pipefail
source /opt/environment/.env
TIMESTAMP=$(date +'%Y-%m-%d_%H%M%S')
sudo -u $USERNAME docker exec -i amap-database-1 pg_dump -F c -U $DB_USER -d $DB_NAME > $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-$TIMESTAMP.$SERVICE_BACKUPS_EXTENSION
ls -1dt $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-*.$SERVICE_BACKUPS_EXTENSION | tail -n +5 | xargs -r rm -f

View File

@@ -0,0 +1,10 @@
#!/bin/bash
set -euo pipefail
source /opt/environment/.env
LATEST_BACKUP=$(ls -1 $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-*.$SERVICE_BACKUPS_EXTENSION 2>/dev/null | sort | tail -n1)
if [ -n "$LATEST_BACKUP" ] && [ -f "$LATEST_BACKUP" ]; then
sudo -u $USERNAME docker exec -i amap-database-1 pg_restore --clean --if-exists -U "$DB_USER" -v -d "$DB_NAME" < $LATEST_BACKUP
fi

View File

41
modules/apps/amap/main.tf Normal file
View File

@@ -0,0 +1,41 @@
module "vm" {
source = "../../vm"
name = var.name
hostname = var.hostname
domain = var.domain
vm_id = var.vm_id
node_name = var.node_name
vm_ip_address = var.vm_ip_address
template_id = var.template_id
cores = var.cores
memory = var.memory
disk_size = var.disk_size
ssh_public_key = var.ssh_public_key
proxmox_host_ip = var.proxmox_host_ip
cloudinit_config = templatefile(
"${path.module}/cloud-init/service.yaml",
{
hostname = var.hostname
domain = var.domain
ssh_key = var.ssh_public_key
proxmox_host_ip = var.proxmox_host_ip
restore-backup-script = indent(6, file("${path.module}/lib/scripts/restore-backup.sh"))
restore-backup-service = indent(6, file("${path.module}/../common/services/docker/restore-backup.service"))
create-backup-script = indent(6, file("${path.module}/lib/scripts/create-backup.sh"))
create-backup-service = indent(6, file("${path.module}}/../common/services/docker/create-backup.service"))
create-backup-timer = indent(6, file("${path.module}/../common/services/create-backup.timer"))
act_runner-service = indent(6, file("${path.module}/../common/services/act_runner.service"))
act_runner-install-script = indent(6, file("${path.module}/../common/scripts/install-runner.sh"))
service-install-script = indent(6, file("${path.module}/../common/scripts/install-service-ci.sh"))
install-docker-script = indent(6, file("${path.module}/../common/scripts/install-docker.sh"))
env-file-content = indent(6, file("${path.module}/.env"))
}
)
}

View File

@@ -0,0 +1,9 @@
output "traefik_service" {
value = [{
domain = var.domain
name = var.name
host = "${var.hostname}"
ip = var.vm_ip_address
port = 80
}]
}

View File

@@ -0,0 +1,71 @@
variable "name" {
description = "Virtual Machine name"
type = string
}
variable "vm_id" {
description = "Virtual Machine id"
type = number
}
variable "node_name" {
description = "Proxmox node name"
type = string
default = "mop"
}
variable "cores" {
description = "Number of CPU cores for this virtual machine"
type = number
default = 2
}
variable "memory" {
description = "Memory RAM for this virtual machine"
type = number
default = 2048
}
variable "balloon" {
description = "Minimum vm memory, using ballooning devide to reach Proxmox node memory target."
type = number
default = 1024
}
variable "template_id" {
description = "Virtual machine template ID"
type = number
}
variable "ssh_public_key" {
description = "Public SSH key for cloud-init user"
type = string
}
variable "hostname" {
description = "Virtual Machine hostname (<service-name>)"
type = string
default = "test"
}
variable "domain" {
description = "Virtual Machine domain (example.fr)"
type = string
default = ""
}
variable "disk_size" {
description = "Disk size for the virtual machine"
type = number
default = 10
}
variable "proxmox_host_ip" {
description = "Proxmox host base ip"
type = string
}
variable "vm_ip_address" {
description = "Virtual machine ip"
type = string
}

View File

@@ -21,8 +21,6 @@ package_upgrade: false
packages:
- git
- nfs-common
- docker.io
- docker-compose
- curl
- jq
@@ -34,6 +32,10 @@ write_files:
permissions: "0644"
content: |
${env-file-content}
- path: /opt/${hostname}/install-docker.sh
permissions: "0755"
content: |
${install-docker-script}
- path: /usr/local/bin/restore-backup.sh
permissions: "0755"
content: |
@@ -73,9 +75,7 @@ runcmd:
- mount -t nfs ${proxmox_host_ip}:/main/backups /backups
- systemctl enable --now create-backup.timer
# Docker setup
- systemctl enable docker
- systemctl start docker
- usermod -aG docker ${hostname}
- /opt/${hostname}/install-docker.sh
# Act_runner install
- /opt/${hostname}/install-runner.sh
- systemctl daemon-reload

View File

@@ -33,6 +33,7 @@ module "vm" {
act_runner-install-script = indent(6, file("${path.module}/../common/scripts/install-runner.sh"))
service-install-script = indent(6, file("${path.module}/../common/scripts/install-service-ci.sh"))
install-docker-script = indent(6, file("${path.module}/../common/scripts/install-docker.sh"))
env-file-content = indent(6, file("${path.module}/.env"))
}

View File

@@ -0,0 +1,23 @@
#!/bin/bash
set -euo pipefail
source /opt/environment/.env
sudo apt update -y
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update -y
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
systemctl enable docker
systemctl start docker
usermod -aG docker $HOSTNAME

View File

@@ -21,8 +21,6 @@ package_upgrade: false
packages:
- git
- nfs-common
- docker.io
- docker-compose
- curl
- jq
@@ -34,6 +32,10 @@ write_files:
permissions: "0644"
content: |
${env-file-content}
- path: /opt/${hostname}/install-docker.sh
permissions: "0755"
content: |
${install-docker-script}
- path: /usr/local/bin/restore-backup.sh
permissions: "0755"
content: |
@@ -73,9 +75,7 @@ runcmd:
- mount -t nfs ${proxmox_host_ip}:/main/backups /backups
- systemctl enable --now create-backup.timer
# Docker setup
- systemctl enable docker
- systemctl start docker
- usermod -aG docker ${hostname}
- /opt/${hostname}/install-docker.sh
# Act_runner install
- /opt/${hostname}/install-runner.sh
- systemctl daemon-reload

View File

@@ -33,6 +33,7 @@ module "vm" {
act_runner-install-script = indent(6, file("${path.module}/../common/scripts/install-runner.sh"))
service-install-script = indent(6, file("${path.module}/../common/scripts/install-service-ci.sh"))
install-docker-script = indent(6, file("${path.module}/../common/scripts/install-docker.sh"))
env-file-content = indent(6, file("${path.module}/.env"))
}

View File

@@ -1,5 +1,6 @@
# Environment files
ENV_FILE_LOCATION=/opt/environment/.env
USERNAME=gateway
# Application Specifics
#openssl rand -hex 20
@@ -9,3 +10,8 @@ TRAEFIK_USER=traefik
TRAEFIK_CONF=/home/traefik/traefik.yml
GATEWAY_REPOSITORY=/Mop/gateway
DYNAMIC_CONFIG_LOCATION=/home/gateway/services.yaml
# Backup specifics
SERVICE_BACKUPS_DIR=/backups/gateway
SERVICE_BACKUPS_PREFIX=gateway-dump
SERVICE_BACKUPS_EXTENSION=db

View File

@@ -24,6 +24,9 @@ packages:
- curl
- nginx
mounts:
- [ "192.168.1.12:/main/backups", "/backups", "nfs", "defaults,_netdev,x-systemd.requires=network-online.target", "0", "0" ]
write_files:
- path: /opt/environment/.env
permissions: "0644"
@@ -33,6 +36,14 @@ write_files:
permissions: "0755"
content: |
${install-traefik-script}
- path: /opt/gateway/install-docker.sh
permissions: "0755"
content: |
${install-docker-script}
- path: /opt/gateway/install-crowdsec.sh
permissions: "0755"
content: |
${install-crowdsec-script}
- path: /etc/systemd/system/traefik.service
permissions: "0755"
content: |
@@ -40,15 +51,47 @@ write_files:
- path: /usr/share/nginx/error-pages/502.html
permissions: "0644"
content: |
{nginx-error-502}
${nginx-error-502}
- path: /etc/nginx/sites-available/default
permissions: "0644"
content: |
{nginx-error-configuration}
${nginx-error-configuration}
- path: /usr/local/bin/restore-backup.sh
permissions: "0755"
content: |
${restore-backup-script}
- path: /etc/systemd/system/restore-backup.service
permissions: "0644"
content: |
${restore-backup-service}
- path: /usr/local/bin/backup.sh
permissions: "0755"
content: |
${create-backup-script}
- path: /etc/systemd/system/create-backup.timer
permissions: "0644"
content: |
${create-backup-timer}
- path: /etc/systemd/system/create-backup.service
permissions: "0644"
content: |
${create-backup-service}
bootcmd:
- hostnamectl set-hostname ${hostname}
runcmd:
# Backup setup
- mkdir -p /backups
- mount -t nfs ${proxmox_host_ip}:/main/backups /backups
- systemctl enable --now create-backup.timer
# Crowdsec / Terraform setup
- /opt/gateway/install-docker.sh
- /opt/gateway/install-crowdsec.sh
- /opt/gateway/install-traefik.sh
- ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled
- systemctl start nginx.service
- systemctl start restore-backup
final_message: |
Base system ready for ${hostname}

View File

@@ -0,0 +1,10 @@
#!/bin/bash
set -euo pipefail
source /opt/environment/.env
TIMESTAMP=$(date +'%Y-%m-%d_%H%M%S')
sudo -u $USERNAME docker cp crowdsec-metabase:/metabase-data/metabase.db/metabase.db.mv.db $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-$TIMESTAMP.$SERVICE_BACKUPS_EXTENSION
ls -1dt $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-*.$SERVICE_BACKUPS_EXTENSION | tail -n +5 | xargs -r rm -f

View File

@@ -0,0 +1,17 @@
set -e
sudo apt update
sudo apt install -y curl gnupg lsb-release
sudo apt install crowdsec
sudo cscli collections install crowdsecurity/traefik
sudo cscli collections install crowdsecurity/http-cve
sudo cscli collections install crowdsecurity/base-http-scenarios
sudo cscli parsers install crowdsecurity/geoip-enrich
sudo systemctl enable crowdsec
sudo systemctl restart crowdsec
sudo cscli hub update
cscli dashboard setup -l 0.0.0.0

View File

@@ -32,6 +32,10 @@ chown $TRAEFIK_USER:$TRAEFIK_USER /etc/traefik/acme.json
chmod 600 /etc/traefik/acme.json
setcap 'cap_net_bind_service=+ep' /usr/local/bin/traefik
sudo mkdir -p /var/log/traefik
sudo touch /var/log/traefik/access.log
sudo chown -R traefik:adm /var/log/traefik
cat > "$TRAEFIK_CONF" <<EOF
entryPoints:
web:
@@ -47,7 +51,9 @@ api:
insecure: false
log:
level: INFO
accessLog: {}
accessLog:
filePath: "/var/log/traefik/access.log"
bufferingSize: 100
certificatesResolvers:
letsencrypt:
acme:

View File

@@ -0,0 +1,10 @@
#!/bin/bash
set -euo pipefail
source /opt/environment/.env
LATEST_BACKUP=$(ls -1 $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-*.$SERVICE_BACKUPS_EXTENSION 2>/dev/null | sort | tail -n1)
if [ -n "$LATEST_BACKUP" ] && [ -f "$LATEST_BACKUP" ]; then
sudo -u $USERNAME docker cp "$LATEST_BACKUP" "crowdsec-metabase:/metabase-data/metabase.db/metabase.db.mv.db"
fi

View File

@@ -25,6 +25,13 @@ module "vm" {
proxmox_host_ip = var.proxmox_host_ip
traefik-service = indent(6, file("${path.module}/lib/services/traefik.service"))
install-traefik-script = indent(6, file("${path.module}/lib/scripts/install-traefik.sh"))
install-crowdsec-script = indent(6, file("${path.module}/lib/scripts/install-crowdsec.sh"))
install-docker-script = indent(6, file("${path.module}/../common/scripts/install-docker.sh"))
restore-backup-script = indent(6, file("${path.module}/lib/scripts/restore-backup.sh"))
restore-backup-service = indent(6, file("${path.module}/../common/services/docker/restore-backup.service"))
create-backup-script = indent(6, file("${path.module}/lib/scripts/create-backup.sh"))
create-backup-service = indent(6, file("${path.module}}/../common/services/docker/create-backup.service"))
create-backup-timer = indent(6, file("${path.module}}/../common/services/create-backup.timer"))
nginx-error-configuration = indent(6, file("${path.module}/lib/scripts/default"))
nginx-error-502 = indent(6, file("${path.module}/lib/scripts/502.html"))
env-file-content = indent(6, file("${path.module}/.env"))

View File

@@ -0,0 +1,22 @@
# Environment files
ENV_FILE_LOCATION=/opt/environment/.env
# gitea act_runner
ACT_RUNNER_VERSION=0.2.13
ACT_RUNNER_LOCATION=/usr/local/bin
ACT_RUNNER_USER=act_runner
GITEA_RUNNER_REGISTRATION_TOKEN=<gitea-repository-runner-token>
USERNAME=girasol
# Gitea instance
GITEA_INSTANCE_URL=https://gitea.aldon.fr
GITEA_SERVICE_APPLICATION_TOKEN=<gitea-auth-token>
GITEA_SERVICE_REPOSITORY=mop/girasol
GITEA_WORKFLOW_NAME=deploy.yaml
# Application specifics
# Backup specifics
SERVICE_BACKUPS_DIR=/backups/girasol
SERVICE_BACKUPS_PREFIX=girasol-dump
SERVICE_BACKUPS_EXTENSION=sql

View File

@@ -0,0 +1,89 @@
#cloud-config
hostname: ${hostname}
local-hostname: ${hostname}
fqdn: ${hostname}.${domain}
manage_etc_hosts: true
users:
- default
- name: ${hostname}
groups: sudo
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ${ssh_key}
disable_root: true
package_update: true
package_upgrade: false
packages:
- git
- nfs-common
- curl
- jq
mounts:
- [ "192.168.1.12:/main/backups", "/backups", "nfs", "defaults,_netdev,x-systemd.requires=network-online.target", "0", "0" ]
write_files:
- path: /opt/environment/.env
permissions: "0644"
content: |
${env-file-content}
- path: /opt/${hostname}/install-docker.sh
permissions: "0755"
content: |
${install-docker-script}
- path: /usr/local/bin/restore-backup.sh
permissions: "0755"
content: |
${restore-backup-script}
- path: /etc/systemd/system/restore-backup.service
permissions: "0644"
content: |
${restore-backup-service}
- path: /usr/local/bin/backup.sh
permissions: "0755"
content: |
${create-backup-script}
- path: /etc/systemd/system/create-backup.timer
permissions: "0644"
content: |
${create-backup-timer}
- path: /etc/systemd/system/create-backup.service
permissions: "0644"
content: |
${create-backup-service}
- path: /etc/systemd/system/act_runner.service
permissions: "0644"
content: |
${act_runner-service}
- path: /opt/${hostname}/install-runner.sh
permissions: "0755"
content: |
${act_runner-install-script}
- path: /opt/${hostname}/install-service.sh
permissions: "0755"
content: |
${service-install-script}
runcmd:
# Backup setup
- mkdir -p /backups
- mount -t nfs ${proxmox_host_ip}:/main/backups /backups
- systemctl enable --now create-backup.timer
# Docker setup
- /opt/${hostname}/install-docker.sh
# Act_runner install
- /opt/${hostname}/install-runner.sh
- systemctl daemon-reload
- systemctl enable act_runner.service
- systemctl start act_runner.service
# ${hostname} install
- /opt/${hostname}/install-service.sh
final_message: |
Base system ready for ${hostname}

View File

@@ -0,0 +1,7 @@
source /opt/environment/.env
TIMESTAMP=$(date +'%Y-%m-%d_%H%M%S')
# create backup file
ls -1dt $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-*.$SERVICE_BACKUPS_EXTENSION | tail -n +5 | xargs -r rm -f

View File

@@ -0,0 +1,5 @@
source /opt/environment/.env
LATEST_BACKUP=$(ls -1 $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-*.$SERVICE_BACKUPS_EXTENSION 2>/dev/null | sort | tail -n1)
# Restore backup file

View File

@@ -0,0 +1,41 @@
module "vm" {
source = "../../vm"
name = var.name
hostname = var.hostname
domain = var.domain
vm_id = var.vm_id
node_name = var.node_name
vm_ip_address = var.vm_ip_address
template_id = var.template_id
cores = var.cores
memory = var.memory
disk_size = var.disk_size
ssh_public_key = var.ssh_public_key
proxmox_host_ip = var.proxmox_host_ip
cloudinit_config = templatefile(
"${path.module}/cloud-init/service.yaml",
{
hostname = var.hostname
domain = var.domain
ssh_key = var.ssh_public_key
proxmox_host_ip = var.proxmox_host_ip
restore-backup-script = indent(6, file("${path.module}/lib/scripts/restore-backup.sh"))
restore-backup-service = indent(6, file("${path.module}/../common/services/docker/restore-backup.service"))
create-backup-script = indent(6, file("${path.module}/lib/scripts/create-backup.sh"))
create-backup-service = indent(6, file("${path.module}}/../common/services/docker/create-backup.service"))
create-backup-timer = indent(6, file("${path.module}/../common/services/create-backup.timer"))
act_runner-service = indent(6, file("${path.module}/../common/services/act_runner.service"))
act_runner-install-script = indent(6, file("${path.module}/../common/scripts/install-runner.sh"))
service-install-script = indent(6, file("${path.module}/../common/scripts/install-service-ci.sh"))
install-docker-script = indent(6, file("${path.module}/../common/scripts/install-docker.sh"))
env-file-content = indent(6, file("${path.module}/.env"))
}
)
}

View File

@@ -0,0 +1,9 @@
output "traefik_service" {
value = [{
domain = var.domain
name = var.name
host = "${var.hostname}"
ip = var.vm_ip_address
port = 80
}]
}

View File

@@ -0,0 +1,71 @@
variable "name" {
description = "Virtual Machine name"
type = string
}
variable "vm_id" {
description = "Virtual Machine id"
type = number
}
variable "node_name" {
description = "Proxmox node name"
type = string
default = "mop"
}
variable "cores" {
description = "Number of CPU cores for this virtual machine"
type = number
default = 2
}
variable "memory" {
description = "Memory RAM for this virtual machine"
type = number
default = 2048
}
variable "balloon" {
description = "Minimum vm memory, using ballooning devide to reach Proxmox node memory target."
type = number
default = 1024
}
variable "template_id" {
description = "Virtual machine template ID"
type = number
}
variable "ssh_public_key" {
description = "Public SSH key for cloud-init user"
type = string
}
variable "hostname" {
description = "Virtual Machine hostname (<service-name>)"
type = string
default = "test"
}
variable "domain" {
description = "Virtual Machine domain (example.fr)"
type = string
default = ""
}
variable "disk_size" {
description = "Disk size for the virtual machine"
type = number
default = 10
}
variable "proxmox_host_ip" {
description = "Proxmox host base ip"
type = string
}
variable "vm_ip_address" {
description = "Virtual machine ip"
type = string
}

View File

@@ -24,8 +24,6 @@ package_upgrade: false
packages:
- git
- nfs-common
- docker.io
- docker-compose
- curl
- unzip
- postgresql
@@ -39,6 +37,10 @@ write_files:
permissions: "0644"
content: |
${env-file-content}
- path: /opt/${hostname}/install-docker.sh
permissions: "0755"
content: |
${install-docker-script}
- path: /usr/local/bin/restore-backup.sh
permissions: "0755"
content: |
@@ -74,9 +76,7 @@ runcmd:
- mount -t nfs ${proxmox_host_ip}:/main/backups /backups
- systemctl enable --now create-backup.timer
# Docker setup
- systemctl enable docker
- systemctl start docker
- usermod -aG docker ${hostname}
- /opt/${hostname}/install-docker.sh
# gitea setup
- /opt/gitea/install-gitea.sh

View File

@@ -31,6 +31,7 @@ module "vm" {
install-gitea-script = indent(6, file("${path.module}/lib/scripts/install-gitea.sh"))
gitea-service = indent(6, file("${path.module}/lib/services/gitea.service"))
install-docker-script = indent(6, file("${path.module}/../common/scripts/install-docker.sh"))
env-file-content = indent(6, file("${path.module}/.env"))
}

View File

@@ -0,0 +1,22 @@
JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
KC_PROXY_HEADERS=forwarded
KC_HTTP_ENABLED=true
KC_DB=postgres
KC_DB_USERNAME=keycloak
KC_DB_PASSWORD=<secret>
KC_HOSTNAME=https://keycloak.aldon.fr
#Running behind reverse proxy that handles TLS
KC_PROXY=edge
#Create admin account on first run
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=<secret>
KEYCLOAK_DIRECTORY=/home/keycloak/keycloak
KEYCLOAK_VERSION=26.2.4
# Backup specifics
SERVICE_BACKUPS_DIR=/backups/keycloak
SERVICE_BACKUPS_PREFIX=keycloak-dump
SERVICE_BACKUPS_EXTENSION=sql

View File

@@ -0,0 +1,71 @@
#cloud-config
hostname: ${hostname}
local-hostname: ${hostname}
fqdn: ${hostname}.${domain}
manage_etc_hosts: true
users:
- default
- name: ${hostname}
groups: sudo
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ${ssh_key}
disable_root: true
package_update: true
package_upgrade: false
packages:
- git
- nfs-common
- curl
- unzip
- openjdk-21-jdk-headless
- postgresql
- postgresql-contrib
mounts:
- [ "192.168.1.12:/main/backups", "/backups", "nfs", "defaults,_netdev,x-systemd.requires=network-online.target", "0", "0" ]
write_files:
- path: /opt/environment/.env
permissions: "0644"
content: |
${env-file-content}
- path: /usr/local/bin/restore-backup.sh
permissions: "0755"
content: |
${restore-backup-script}
- path: /etc/systemd/system/restore-backup.service
permissions: "0644"
content: |
${restore-backup-service}
- path: /usr/local/bin/backup.sh
permissions: "0755"
content: |
${create-backup-script}
- path: /etc/systemd/system/create-backup.timer
permissions: "0644"
content: |
${create-backup-timer}
- path: /etc/systemd/system/create-backup.service
permissions: "0644"
content: |
${create-backup-service}
- path: /etc/systemd/system/keycloak.service
permissions: "0644"
content: |
${keycloak-service}
runcmd:
# Backup setup
- mkdir -p /backups
- mount -t nfs ${proxmox_host_ip}:/main/backups /backups
- systemctl enable --now create-backup.timer
final_message: |
Base system ready for ${hostname}

View File

@@ -0,0 +1,18 @@
#!/bin/bash
set -euo pipefail
source /opt/environment/.env
TIMESTAMP=$(date +'%Y-%m-%d_%H%M%S')
# Backup realms
sh $KEYCLOAK_DIRECTORY/kc.sh export --file $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-$TIMESTAMP.json
# Backup database
pg_dump -U keycloak -F c -b -v -f $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-$TIMESTAMP.$SERVICE_BACKUPS_EXTENSION
ls -1dt $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-*.$SERVICE_BACKUPS_EXTENSION | tail -n +5 | xargs -r rm -f
ls -1dt $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-*.json | tail -n +5 | xargs -r rm -f

View File

@@ -0,0 +1,34 @@
#//bin/bash
# KEYCLOAK_DIRECTORY=/home/keycloak/keycloak
# KEYCLOAK_VERSION: 26.2.4
# KC_DB_PASSWORD
# SERVICE_BACKUPS_DIR
# SERVICE_BACKUPS_PREFIX
# SERVICE_BACKUPS_EXTENSION
if [ ! -f $KEYCLOAK_DIRECTORY ]; then
wget -O /tmp/keycloak.zip https://github.com/keycloak/keycloak/releases/download/$KEYCLOAK_VERSION/keycloak-$KEYCLOAK_VERSION.zip
unzip -o /tmp/keycloak /tmp/keycloak.zip -d /tmp
mv /tmp/keycloak $KEYCLOAK_DIRECTORY
chmod o+x $KEYCLOAK_DIRECTORY/bin
fi
groupadd keycloak
sudo -u postgres psql <<EOF
CREATE DATABASE keycloak with encoding 'UTF8' TEMPLATE template0;
CREATE USER keycloak WITH ENCRYPTED PASSWORD '$KC_DB_PASSWORD';
GRANT ALL PRIVILEGES ON DATABASE keycloak TO keycloak;
EOF
if ls -1 "$SERVICE_BACKUPS_DIR"/$SERVICE_BACKUPS_PREFIX-*.$SERVICE_BACKUPS_EXTENSION >/dev/null 2>&1; then
echo "---- Backup found, restoring Gitea ----"
/usr/local/bin/restore-backup.sh
else
echo "---- No backup found in $SERVICE_BACKUPS_DIR, skipping restore ----"
fi
sudo systemctl enable keycloak
sudo systemctl start keycloak

View File

@@ -0,0 +1,14 @@
#!/bin/bash
source /opt/environment/.env
LATEST_BACKUP_DB=$(ls -1 $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-*.$SERVICE_BACKUPS_EXTENSION 2>/dev/null | sort | tail -n1)
LATEST_BACKUP_REALMS=$(ls -1 $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-*.json 2>/dev/null | sort | tail -n1)
if [ -n "$LATEST_BACKUP_DB" ] && [ -f "$LATEST_BACKUP_DB" ]; then
psql -U keycloak $KC_DB < $LATEST_BACKUP_DB
fi
if [ -n "$LATEST_BACKUP_REALMS" ] && [ -f "$LATEST_BACKUP_REALMS" ]; then
sh $KEYCLOAK_DIRECTORY/kc.sh import --file $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-$TIMESTAMP.json
fi

View File

@@ -0,0 +1,9 @@
[Unit]
Description=Backup Service
Wants=network.target
After=network.target
[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/backup.sh

View File

@@ -0,0 +1,17 @@
[Unit]
Description=Keycloak Authorization Server
Requires=network.target
After=syslog.target network.target
[Service]
Type=idle
User=keycloak
Group=keycloak
ExecStart=/home/keycloak/keycloak/bin/kc.sh start
ExecStop=/home/keycloak/keycloak/bin/kc.sh stop
Restart=always
RestartSec=15
EnvironmentFile=/opt/environment/.env
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,9 @@
[Unit]
Description=Restore latest backup
After=network.target postgresql.service
Requires=postgresql.service
[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/restore-backup.sh

View File

@@ -0,0 +1,35 @@
module "vm" {
source = "../../vm"
name = var.name
hostname = var.hostname
domain = var.domain
vm_id = var.vm_id
node_name = var.node_name
vm_ip_address = var.vm_ip_address
template_id = var.template_id
cores = var.cores
memory = var.memory
disk_size = var.disk_size
ssh_public_key = var.ssh_public_key
proxmox_host_ip = var.proxmox_host_ip
cloudinit_config = templatefile(
"${path.module}/cloud-init/service.yaml",
{
hostname = var.hostname
domain = var.domain
ssh_key = var.ssh_public_key
proxmox_host_ip = var.proxmox_host_ip
restore-backup-script = indent(6, file("${path.module}/lib/scripts/restore-backup.sh"))
restore-backup-service = indent(6, file("${path.module}/lib/services/restore-backup.service"))
create-backup-script = indent(6, file("${path.module}/lib/scripts/create-backup.sh"))
create-backup-service = indent(6, file("${path.module}/lib/services/create-backup.service"))
create-backup-timer = indent(6, file("${path.module}/../common/services/create-backup.timer"))
env-file-content = indent(6, file("${path.module}/.env"))
}
)
}

View File

@@ -0,0 +1,9 @@
output "traefik_service" {
value = [{
domain = var.domain
name = var.name
host = "${var.hostname}"
ip = var.vm_ip_address
port = 8080
}]
}

View File

@@ -0,0 +1,71 @@
variable "name" {
description = "Virtual Machine name"
type = string
}
variable "vm_id" {
description = "Virtual Machine id"
type = number
}
variable "node_name" {
description = "Proxmox node name"
type = string
default = "mop"
}
variable "cores" {
description = "Number of CPU cores for this virtual machine"
type = number
default = 2
}
variable "memory" {
description = "Memory RAM for this virtual machine"
type = number
default = 2048
}
variable "balloon" {
description = "Minimum vm memory, using ballooning devide to reach Proxmox node memory target."
type = number
default = 1024
}
variable "template_id" {
description = "Virtual machine template ID"
type = number
}
variable "ssh_public_key" {
description = "Public SSH key for cloud-init user"
type = string
}
variable "hostname" {
description = "Virtual Machine hostname (<service-name>)"
type = string
default = "test"
}
variable "domain" {
description = "Virtual Machine domain (example.fr)"
type = string
default = ""
}
variable "disk_size" {
description = "Disk size for the virtual machine"
type = number
default = 10
}
variable "proxmox_host_ip" {
description = "Proxmox host base ip"
type = string
}
variable "vm_ip_address" {
description = "Virtual machine ip"
type = string
}

View File

View File

@@ -0,0 +1,91 @@
#cloud-config
hostname: ${hostname}
local-hostname: ${hostname}
fqdn: ${hostname}.${domain}
manage_etc_hosts: true
groups:
- git
users:
- default
- name: ${hostname}
groups: sudo,git
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ${ssh_key}
disable_root: true
package_update: true
package_upgrade: false
packages:
- git
- nfs-common
- curl
- ca-certificates
- gnupg
- unzip
- postgresql
- postgresql-client
mounts:
- [ "192.168.1.12:/main/backups", "/backups", "nfs", "defaults,_netdev,x-systemd.requires=network-online.target", "0", "0" ]
write_files:
- path: /opt/environment/.env
permissions: "0644"
content: |
${env-file-content}
- path: /usr/local/bin/restore-backup.sh
permissions: "0755"
content: |
${restore-backup-script}
- path: /etc/systemd/system/restore-backup.service
permissions: "0644"
content: |
${restore-backup-service}
- path: /usr/local/bin/backup.sh
permissions: "0755"
content: |
${create-backup-script}
- path: /etc/systemd/system/create-backup.timer
permissions: "0644"
content: |
${create-backup-timer}
- path: /etc/systemd/system/create-backup.service
permissions: "0644"
content: |
${create-backup-service}
- path: /opt/n8n/install-n8n.sh
permissions: "0755"
content: |
${install-n8n-script}
- path: /opt/n8n/install-docker.sh
permissions: "0755"
content: |
${install-docker-script}
- path: /home/n8n/docker-compose.yaml
permissions: "0755"
content: |
${n8n-docker-compose}
- path: /home/n8n/init-data.sh
permissions: "0755"
content: |
${n8n-init-data}
runcmd:
# Docker install
- /opt/n8n/install-docker.sh
# Backup setup
- mkdir -p /backups
- mount -t nfs ${proxmox_host_ip}:/main/backups /backups
- systemctl enable --now create-backup.timer
# Install n8n
- /opt/n8n/install-n8n.sh
final_message: |
Base system ready for ${hostname}

View File

@@ -0,0 +1,10 @@
#!/bin/bash
set -euo pipefail
source /opt/environment/.env
TIMESTAMP=$(date +'%Y-%m-%d_%H%M%S')
sudo -u $USERNAME docker exec -i n8n-postgres-1 pg_dump -F c -U $DB_USER -d $DB_NAME > $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-$TIMESTAMP.$SERVICE_BACKUPS_EXTENSION
ls -1dt $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-*.$SERVICE_BACKUPS_EXTENSION | tail -n +5 | xargs -r rm -f

View File

@@ -0,0 +1,8 @@
#!/bin/bash
set -e
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
CREATE USER $POSTGRES_NON_ROOT_USER WITH PASSWORD '$POSTGRES_NON_ROOT_PASSWORD';
CREATE DATABASE $POSTGRES_DB;
GRANT ALL PRIVILEGES ON DATABASE $POSTGRES_DB TO $POSTGRES_NON_ROOT_USER;
EOSQL

View File

@@ -0,0 +1,11 @@
#!/bin/bash
#https://docs.n8n.io/hosting/installation/docker/#using-with-postgresql
set -euo pipefail
source /opt/environment/.env
cd $SERVICE_WORKDIR
cp /opt/environment/.env $SERVICE_WORKDIR/.env
docker-compose up -d

View File

@@ -0,0 +1,10 @@
#!/bin/bash
set -euo pipefail
source /opt/environment/.env
LATEST_BACKUP=$(ls -1 $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-*.$SERVICE_BACKUPS_EXTENSION 2>/dev/null | sort | tail -n1)
if [ -n "$LATEST_BACKUP" ] && [ -f "$LATEST_BACKUP" ]; then
sudo -u $USERNAME docker exec -i n8n-postgres-1 pg_restore --clean --if-exists -U "$DB_USER" -v -d "$DB_NAME" < $LATEST_BACKUP
fi

View File

@@ -0,0 +1,54 @@
volumes:
db_storage:
n8n_storage:
services:
postgres:
image: postgres:16
restart: always
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_NON_ROOT_USER=${POSTGRES_NON_ROOT_USER}
- POSTGRES_NON_ROOT_PASSWORD=${POSTGRES_NON_ROOT_PASSWORD}
volumes:
- db_storage:/var/lib/postgresql/data
- ./init-data.sh:/docker-entrypoint-initdb.d/init-data.sh
healthcheck:
test: ['CMD-SHELL', 'pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}']
interval: 5s
timeout: 5s
retries: 10
n8n:
image: docker.n8n.io/n8nio/n8n:${N8N_VERSION}
restart: always
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- N8N_RUNNERS_MODE=external
- N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
- N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0
ports:
- 5678:5678
links:
- postgres
volumes:
- n8n_storage:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
n8n-runner:
image: n8nio/runners:${N8N_VERSION}
restart: always
environment:
- N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
- N8N_RUNNERS_TASK_BROKER_URI=http://n8n:5679
depends_on:
- n8n

39
modules/apps/n8n/main.tf Normal file
View File

@@ -0,0 +1,39 @@
module "vm" {
source = "../../vm"
name = var.name
hostname = var.hostname
domain = var.domain
vm_id = var.vm_id
node_name = var.node_name
vm_ip_address = var.vm_ip_address
template_id = var.template_id
cores = var.cores
memory = var.memory
disk_size = var.disk_size
ssh_public_key = var.ssh_public_key
proxmox_host_ip = var.proxmox_host_ip
cloudinit_config = templatefile(
"${path.module}/cloud-init/service.yaml",
{
hostname = var.hostname
domain = var.domain
ssh_key = var.ssh_public_key
proxmox_host_ip = var.proxmox_host_ip
restore-backup-script = indent(6, file("${path.module}/lib/scripts/restore-backup.sh"))
restore-backup-service = indent(6, file("${path.module}/../common/services/docker/restore-backup.service"))
create-backup-script = indent(6, file("${path.module}/lib/scripts/create-backup.sh"))
create-backup-timer = indent(6, file("${path.module}/../common/services/create-backup.timer"))
create-backup-service = indent(6, file("${path.module}/../common/services/docker/create-backup.service"))
install-n8n-script = indent(6, file("${path.module}/lib/scripts/install-n8n.sh"))
install-docker-script = indent(6, file("${path.module}/../common/scripts/install-docker.sh"))
n8n-docker-compose = indent(6, file("${path.module}/lib/services/docker-compose.yaml"))
n8n-init-data = indent(6, file("${path.module}/lib/scripts/init-data.sh"))
env-file-content = indent(6, file("${path.module}/.env"))
}
)
}

View File

@@ -0,0 +1,9 @@
output "traefik_service" {
value = [{
domain = var.domain
name = var.name
host = "${var.hostname}"
ip = var.vm_ip_address
port = 5678
}]
}

View File

@@ -0,0 +1,71 @@
variable "name" {
description = "Virtual Machine name"
type = string
}
variable "vm_id" {
description = "Virtual Machine id"
type = number
}
variable "node_name" {
description = "Proxmox node name"
type = string
default = "mop"
}
variable "cores" {
description = "Number of CPU cores for this virtual machine"
type = number
default = 2
}
variable "memory" {
description = "Memory RAM for this virtual machine"
type = number
default = 2048
}
variable "balloon" {
description = "Minimum vm memory, using ballooning devide to reach Proxmox node memory target."
type = number
default = 1024
}
variable "template_id" {
description = "Virtual machine template ID"
type = number
}
variable "ssh_public_key" {
description = "Public SSH key for cloud-init user"
type = string
}
variable "hostname" {
description = "Virtual Machine hostname (<service-name>)"
type = string
default = "test"
}
variable "domain" {
description = "Virtual Machine domain (example.fr)"
type = string
default = ""
}
variable "disk_size" {
description = "Disk size for the virtual machine"
type = number
default = 10
}
variable "proxmox_host_ip" {
description = "Proxmox host base ip"
type = string
}
variable "vm_ip_address" {
description = "Virtual machine ip"
type = string
}

View File

View File

View File

View File

@@ -0,0 +1,15 @@
# Environment files
ENV_FILE_LOCATION=/opt/environment/.env
# Application specifics
DATABASE_URL=postgres://postgres:changeme@db/windmill?sslmode=disable
DB_USER=postgres
DB_NAME=windmill
DB_PASS=
WM_IMAGE=ghcr.io/windmill-labs/windmill:main
SERVICE_WORKDIR=/home/windmill
# Backup specifics
SERVICE_BACKUPS_DIR=/backups/windmill
SERVICE_BACKUPS_PREFIX=windmill-dump
SERVICE_BACKUPS_EXTENSION=dump

View File

@@ -0,0 +1,89 @@
#cloud-config
hostname: ${hostname}
local-hostname: ${hostname}
fqdn: ${hostname}.${domain}
manage_etc_hosts: true
groups:
- git
users:
- default
- name: ${hostname}
groups: sudo,git
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ${ssh_key}
disable_root: true
package_update: true
package_upgrade: false
packages:
- git
- nfs-common
- curl
- unzip
- postgresql
- postgresql-client
mounts:
- [ "192.168.1.12:/main/backups", "/backups", "nfs", "defaults,_netdev,x-systemd.requires=network-online.target", "0", "0" ]
write_files:
- path: /opt/environment/.env
permissions: "0644"
content: |
${env-file-content}
- path: /opt/${hostname}/install-docker.sh
permissions: "0755"
content: |
${install-docker-script}
- path: /usr/local/bin/restore-backup.sh
permissions: "0755"
content: |
${restore-backup-script}
- path: /etc/systemd/system/restore-backup.service
permissions: "0644"
content: |
${restore-backup-service}
- path: /usr/local/bin/backup.sh
permissions: "0755"
content: |
${create-backup-script}
- path: /etc/systemd/system/create-backup.timer
permissions: "0644"
content: |
${create-backup-timer}
- path: /etc/systemd/system/create-backup.service
permissions: "0644"
content: |
${create-backup-service}
- path: /opt/windmill/install-windmill.sh
permissions: "0755"
content: |
${install-windmill-script}
- path: /home/windmill/docker-compose.yaml
permissions: "0755"
content: |
${windmill-docker-compose}
- path: /home/windmill/Caddyfile
permissions: "0755"
content: |
${windmill-caddyfile}
runcmd:
# Backup setup
- mkdir -p /backups
- mount -t nfs ${proxmox_host_ip}:/main/backups /backups
- systemctl enable --now create-backup.timer
# Docker setup
- /opt/${hostname}/install-docker.sh
# windmill setup
- /opt/windmill/install-windmill.sh
final_message: |
Base system ready for ${hostname}

View File

@@ -0,0 +1,15 @@
#!/bin/bash
#SERVICE_BACKUPS_DIR
#SERVICE_BACKUPS_PREFIX
#SERVICE_BACKUPS_EXTENSION
set -euo pipefail
source /opt/environment/.env
TIMESTAMP=$(date +'%Y-%m-%d_%H%M%S')
# docker exec $DB_SERVICE
ls -1dt $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-*.$SERVICE_BACKUPS_EXTENSION | tail -n +5 | xargs -r rm -f

View File

@@ -0,0 +1,11 @@
#!/bin/bash
#https://www.windmill.dev/docs/advanced/self_host
set -euo pipefail
source /opt/environment/.env
cd $SERVICE_WORKDIR
cp /opt/environment/.env $SERVICE_WORKDIR/.env
docker-compose up -d

View File

@@ -0,0 +1,20 @@
#!/bin/bash
#USERNAME
#SERVICE_BACKUPS_DIR
#SERVICE_BACKUPS_PREFIX
#SERVICE_BACKUPS_EXTENSION
#DB_USER
#DB_NAME
set -euo pipefail
source /opt/environment/.env
LATEST_BACKUP=$(ls -1 $SERVICE_BACKUPS_DIR/$SERVICE_BACKUPS_PREFIX-*.$SERVICE_BACKUPS_EXTENSION 2>/dev/null | sort | tail -n1)
if [ -n "$LATEST_BACKUP" ] && [ -f "$LATEST_BACKUP" ]; then
systemctl stop listmonk
sudo -u $USERNAME pg_restore --clean --if-exists -U "$DB_USER" -d "$DB_NAME" $LATEST_BACKUP
systemctl start listmonk
fi

View File

@@ -0,0 +1,35 @@
{
layer4 {
:25 {
proxy {
to windmill_server:2525
}
}
}
}
{$BASE_URL} {
bind {$ADDRESS}
# LSP - Language Server Protocol for code intelligence (windmill_extra:3001)
reverse_proxy /ws/* http://windmill_extra:3001
# Multiplayer - Real-time collaboration, Enterprise Edition (windmill_extra:3002)
# Uncomment and set ENABLE_MULTIPLAYER=true in docker-compose.yml
# reverse_proxy /ws_mp/* http://windmill_extra:3002
# Debugger - Interactive debugging via DAP WebSocket (windmill_extra:3003)
# Set ENABLE_DEBUGGER=true in docker-compose.yml to enable
handle_path /ws_debug/* {
reverse_proxy http://windmill_extra:3003
}
# Search indexer, Enterprise Edition (windmill_indexer:8002)
# reverse_proxy /api/srch/* http://windmill_indexer:8002
# Default: Windmill server
reverse_proxy /* http://windmill_server:8000
# TLS with custom certificates
# tls /certs/cert.pem /certs/key.pem
}

View File

@@ -0,0 +1,222 @@
version: "3.7"
x-logging: &default-logging
driver: "json-file"
options:
max-size: "${LOG_MAX_SIZE:-20m}"
max-file: "${LOG_MAX_FILE:-10}"
compress: "true"
services:
db:
deploy:
# To use an external database, set replicas to 0 and set DATABASE_URL to the external database url in the .env file
replicas: 1
image: postgres:16
shm_size: 1g
restart: unless-stopped
volumes:
- db_data:/var/lib/postgresql/data
expose:
- 5432
environment:
POSTGRES_PASSWORD: ${DB_PASS}
POSTGRES_DB: ${DB_NAME}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
logging: *default-logging
windmill_server:
image: ${WM_IMAGE}
pull_policy: always
deploy:
replicas: 1
restart: unless-stopped
expose:
- 8000
- 2525
environment:
- DATABASE_URL=${DATABASE_URL}
- MODE=server
depends_on:
db:
condition: service_healthy
volumes:
- worker_logs:/tmp/windmill/logs
logging: *default-logging
windmill_worker:
image: ${WM_IMAGE}
pull_policy: always
deploy:
replicas: 3
resources:
limits:
cpus: "1"
memory: 2048M
# for GB, use syntax '2Gi'
restart: unless-stopped
# Uncomment to enable PID namespace isolation (recommended for security)
# Requires privileged mode for --mount-proc flag
# See: https://www.windmill.dev/docs/advanced/security_isolation
# privileged: true
environment:
- DATABASE_URL=${DATABASE_URL}
- MODE=worker
- WORKER_GROUP=default
# If running with non-root/non-windmill UID (e.g., user: "1001:1001"),
# add: - HOME=/tmp
# Uncomment to enable PID namespace isolation (requires privileged: true above)
# - ENABLE_UNSHARE_PID=true
depends_on:
db:
condition: service_healthy
# to mount the worker folder to debug, KEEP_JOB_DIR=true and mount /tmp/windmill
volumes:
# mount the docker socket to allow to run docker containers from within the workers
- /var/run/docker.sock:/var/run/docker.sock
- worker_dependency_cache:/tmp/windmill/cache
- worker_logs:/tmp/windmill/logs
logging: *default-logging
## This worker is specialized for "native" jobs. Native jobs run in-process and thus are much more lightweight than other jobs
windmill_worker_native:
# Use ghcr.io/windmill-labs/windmill-ee:main for the ee
image: ${WM_IMAGE}
pull_policy: always
deploy:
replicas: 1
resources:
limits:
cpus: "1"
memory: 2048M
# for GB, use syntax '2Gi'
restart: unless-stopped
# Uncomment to enable PID namespace isolation (recommended for security)
# Requires privileged mode for --mount-proc flag
# See: https://www.windmill.dev/docs/advanced/security_isolation
# privileged: true
environment:
- DATABASE_URL=${DATABASE_URL}
- MODE=worker
- WORKER_GROUP=native
- NUM_WORKERS=8
- SLEEP_QUEUE=200
# Uncomment to enable PID namespace isolation (requires privileged: true above)
# - ENABLE_UNSHARE_PID=true
depends_on:
db:
condition: service_healthy
volumes:
- worker_logs:/tmp/windmill/logs
logging: *default-logging
# This worker is specialized for reports or scraping jobs. It is assigned the "reports" worker group which has an init script that installs chromium and can be targeted by using the "chromium" worker tag.
# windmill_worker_reports:
# image: ${WM_IMAGE}
# pull_policy: always
# deploy:
# replicas: 1
# resources:
# limits:
# cpus: "1"
# memory: 2048M
# # for GB, use syntax '2Gi'
# restart: unless-stopped
# # Uncomment to enable PID namespace isolation (recommended for security)
# # Requires privileged mode for --mount-proc flag
# # See: https://www.windmill.dev/docs/advanced/security_isolation
# # privileged: true
# environment:
# - DATABASE_URL=${DATABASE_URL}
# - MODE=worker
# - WORKER_GROUP=reports
# # Uncomment to enable PID namespace isolation (requires privileged: true above)
# # - ENABLE_UNSHARE_PID=true
# depends_on:
# db:
# condition: service_healthy
# # to mount the worker folder to debug, KEEP_JOB_DIR=true and mount /tmp/windmill
# volumes:
# # mount the docker socket to allow to run docker containers from within the workers
# - /var/run/docker.sock:/var/run/docker.sock
# - worker_dependency_cache:/tmp/windmill/cache
# - worker_logs:/tmp/windmill/logs
# The indexer powers full-text job and log search, an EE feature.
windmill_indexer:
image: ${WM_IMAGE}
pull_policy: always
deploy:
replicas: 0 # set to 1 to enable full-text job and log search
restart: unless-stopped
expose:
- 8002
environment:
- PORT=8002
- DATABASE_URL=${DATABASE_URL}
- MODE=indexer
depends_on:
db:
condition: service_healthy
volumes:
- windmill_index:/tmp/windmill/search
- worker_logs:/tmp/windmill/logs
logging: *default-logging
# Combined extra services: LSP, Multiplayer, and Debugger
# Each service can be enabled/disabled via environment variables:
# - ENABLE_LSP=true (default) - Language Server Protocol for code intelligence
# - ENABLE_MULTIPLAYER=false - Real-time collaboration (Enterprise Edition)
# - ENABLE_DEBUGGER=false - Interactive debugging via DAP WebSocket
windmill_extra:
image: ghcr.io/windmill-labs/windmill-extra:latest
pull_policy: always
restart: unless-stopped
expose:
- 3001 # LSP
- 3002 # Multiplayer
- 3003 # Debugger
environment:
- ENABLE_LSP=true
- ENABLE_MULTIPLAYER=false # Set to true to enable multiplayer (Enterprise Edition)
- ENABLE_DEBUGGER=true # Set to true to enable debugger
- DEBUGGER_PORT=3003 # Debugger service port
- ENABLE_NSJAIL=false # Set to true for nsjail sandboxing (requires privileged: true)
- REQUIRE_SIGNED_DEBUG_REQUESTS=false # Set to true to require JWT tokens for debug sessions
- WINDMILL_BASE_URL=http://windmill_server:8000
volumes:
- lsp_cache:/pyls/.cache
logging: *default-logging
caddy:
image: ghcr.io/windmill-labs/caddy-l4:latest
restart: unless-stopped
# Configure the mounted Caddyfile and the exposed ports or use another reverse proxy if needed
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
# - ./certs:/certs # Provide custom certificate files like cert.pem and key.pem to enable HTTPS - See the corresponding section in the Caddyfile
ports:
# To change the exposed port, simply change 80:80 to <desired_port>:80. No other changes needed
- 80:80
- 25:25
# - 443:443 # Uncomment to enable HTTPS handling by Caddy
environment:
- BASE_URL=":80"
# - BASE_URL=":443" # uncomment and comment line above to enable HTTPS via custom certificate and key files
# - BASE_URL=mydomain.com # Uncomment and comment line above to enable HTTPS handling by Caddy
logging: *default-logging
volumes:
db_data: null
worker_dependency_cache: null
worker_logs: null
worker_memory: null
windmill_index: null
lsp_cache: null
caddy_data: null

View File

@@ -0,0 +1,41 @@
module "vm" {
source = "../../vm"
name = var.name
hostname = var.hostname
domain = var.domain
vm_id = var.vm_id
node_name = var.node_name
vm_ip_address = var.vm_ip_address
template_id = var.template_id
cores = var.cores
memory = var.memory
disk_size = var.disk_size
ssh_public_key = var.ssh_public_key
proxmox_host_ip = var.proxmox_host_ip
cloudinit_config = templatefile(
"${path.module}/cloud-init/service.yaml",
{
hostname = var.hostname
domain = var.domain
ssh_key = var.ssh_public_key
proxmox_host_ip = var.proxmox_host_ip
restore-backup-script = indent(6, file("${path.module}/lib/scripts/restore-backup.sh"))
restore-backup-service = indent(6, file("${path.module}/../common/services/docker/restore-backup.service"))
create-backup-script = indent(6, file("${path.module}/lib/scripts/create-backup.sh"))
create-backup-timer = indent(6, file("${path.module}/../common/services/create-backup.timer"))
create-backup-service = indent(6, file("${path.module}/../common/services/docker/create-backup.service"))
install-windmill-script = indent(6, file("${path.module}/lib/scripts/install-windmill.sh"))
windmill-docker-compose = indent(6, file("${path.module}/lib/services/docker-compose.yaml"))
windmill-caddyfile = indent(6, file("${path.module}/lib/services/Caddyfile"))
install-docker-script = indent(6, file("${path.module}/../common/scripts/install-docker.sh"))
env-file-content = indent(6, file("${path.module}/.env"))
}
)
}

View File

@@ -0,0 +1,9 @@
output "traefik_service" {
value = [{
domain = var.domain
name = var.name
host = "${var.hostname}"
ip = var.vm_ip_address
port = 80
}]
}

View File

@@ -0,0 +1,71 @@
variable "name" {
description = "Virtual Machine name"
type = string
}
variable "vm_id" {
description = "Virtual Machine id"
type = number
}
variable "node_name" {
description = "Proxmox node name"
type = string
default = "mop"
}
variable "cores" {
description = "Number of CPU cores for this virtual machine"
type = number
default = 2
}
variable "memory" {
description = "Memory RAM for this virtual machine"
type = number
default = 2048
}
variable "balloon" {
description = "Minimum vm memory, using ballooning devide to reach Proxmox node memory target."
type = number
default = 1024
}
variable "template_id" {
description = "Virtual machine template ID"
type = number
}
variable "ssh_public_key" {
description = "Public SSH key for cloud-init user"
type = string
}
variable "hostname" {
description = "Virtual Machine hostname (<service-name>)"
type = string
default = "test"
}
variable "domain" {
description = "Virtual Machine domain (example.fr)"
type = string
default = ""
}
variable "disk_size" {
description = "Disk size for the virtual machine"
type = number
default = 10
}
variable "proxmox_host_ip" {
description = "Proxmox host base ip"
type = string
}
variable "vm_ip_address" {
description = "Virtual machine ip"
type = string
}

View File

@@ -56,6 +56,23 @@ http:
certResolver: letsencrypt
middlewares:
- redirect-errors
mathieu:
rule: "Host(`mathieu.wiki`)"
entryPoints:
- websecure
service: noop
tls:
certResolver: letsencrypt
middlewares:
- redirect-to-mathieu
- redirect-errors
mathieu-http:
rule: "Host(`mathieu.wiki`)"
entryPoints:
- web
middlewares:
- redirect-to-mathieu
service: noop
wiki:
rule: "Host(`benoit.mathieu.wiki`)"
entryPoints:
@@ -131,6 +148,11 @@ http:
servers:
- url: "http://127.0.0.1:8090"
middlewares:
redirect-to-mathieu:
redirectRegex:
regex: "^https?://mathieu.wiki/(.*)"
replacement: "https://benoit.mathieu.wiki/$1"
permanent: true
redirect-to-aldon:
redirectRegex:
regex: "^https?://aldon.fr/(.*)"