feat(tf-services): shared services droplet (3 Forgejo + Verdaccio) module
DO droplet (nyc3 s-2vcpu-4gb + swap) running 3 co-located Forgejo (ct/mc/quinn) + Verdaccio via docker-compose. HTTP+token (built-in SSH disabled). Provisioned 165.227.191.38; state local (gitignored). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
commit
870bb55174
5 changed files with 207 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.terraform/
|
||||
*.tfstate
|
||||
*.tfstate.backup
|
||||
.terraform.lock.hcl
|
||||
terraform.tfvars
|
||||
72
cloud-init.yaml
Normal file
72
cloud-init.yaml
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#cloud-config
|
||||
package_update: true
|
||||
packages:
|
||||
- docker.io
|
||||
- docker-compose-v2
|
||||
|
||||
write_files:
|
||||
- path: /opt/services/docker-compose.yml
|
||||
permissions: "0644"
|
||||
content: |
|
||||
services:
|
||||
forgejo-ct:
|
||||
image: codeberg.org/forgejo/forgejo:10
|
||||
restart: always
|
||||
environment:
|
||||
USER_UID: "1000"
|
||||
USER_GID: "1000"
|
||||
FORGEJO__server__HTTP_PORT: "3000"
|
||||
FORGEJO__server__SSH_PORT: "2222"
|
||||
FORGEJO__security__INSTALL_LOCK: "true"
|
||||
FORGEJO__service__DISABLE_REGISTRATION: "true"
|
||||
volumes:
|
||||
- /opt/services/ct:/data
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "2222:22"
|
||||
forgejo-mc:
|
||||
image: codeberg.org/forgejo/forgejo:10
|
||||
restart: always
|
||||
environment:
|
||||
USER_UID: "1000"
|
||||
USER_GID: "1000"
|
||||
FORGEJO__server__HTTP_PORT: "3000"
|
||||
FORGEJO__server__SSH_PORT: "2223"
|
||||
FORGEJO__security__INSTALL_LOCK: "true"
|
||||
FORGEJO__service__DISABLE_REGISTRATION: "true"
|
||||
volumes:
|
||||
- /opt/services/mc:/data
|
||||
ports:
|
||||
- "3001:3000"
|
||||
- "2223:22"
|
||||
forgejo-quinn:
|
||||
image: codeberg.org/forgejo/forgejo:10
|
||||
restart: always
|
||||
environment:
|
||||
USER_UID: "1000"
|
||||
USER_GID: "1000"
|
||||
FORGEJO__server__HTTP_PORT: "3000"
|
||||
FORGEJO__server__SSH_PORT: "2224"
|
||||
FORGEJO__security__INSTALL_LOCK: "true"
|
||||
FORGEJO__service__DISABLE_REGISTRATION: "true"
|
||||
volumes:
|
||||
- /opt/services/quinn:/data
|
||||
ports:
|
||||
- "3002:3000"
|
||||
- "2224:22"
|
||||
verdaccio:
|
||||
image: verdaccio/verdaccio:6
|
||||
restart: always
|
||||
ports:
|
||||
- "4873:4873"
|
||||
volumes:
|
||||
- /opt/services/verdaccio:/verdaccio/storage
|
||||
|
||||
runcmd:
|
||||
# 2GB swap (safety on the 4GB box)
|
||||
- [ bash, -c, "fallocate -l 2G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile && echo '/swapfile none swap sw 0 0' >> /etc/fstab" ]
|
||||
- [ bash, -c, "mkdir -p /opt/services/ct /opt/services/mc /opt/services/quinn /opt/services/verdaccio && chown -R 1000:1000 /opt/services" ]
|
||||
- [ systemctl, enable, --now, docker ]
|
||||
- [ bash, -c, "cd /opt/services && docker compose up -d" ]
|
||||
|
||||
final_message: "services droplet up: 3 Forgejo (ct:3000 mc:3001 quinn:3002) + Verdaccio:4873"
|
||||
88
main.tf
Normal file
88
main.tf
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
###############################################################################
|
||||
# Shared services droplet — 3 Forgejo instances (ct/mc/quinn) + Verdaccio,
|
||||
# co-located. Public-facing (like the current ct-forge). 4GB + swap (tight but
|
||||
# fine for low-traffic git + npm). PyPI / SwiftPM / DNS / Caddy are fast-follow.
|
||||
###############################################################################
|
||||
|
||||
resource "digitalocean_droplet" "services" {
|
||||
name = var.name
|
||||
image = "ubuntu-24-04-x64"
|
||||
size = var.droplet_size
|
||||
region = var.region
|
||||
ssh_keys = var.ssh_key_fingerprints
|
||||
tags = ["services", "forge", "quinn-infra"]
|
||||
|
||||
user_data = file("${path.module}/cloud-init.yaml")
|
||||
|
||||
lifecycle {
|
||||
# Forgejo/Verdaccio data lives in /opt/services volumes; never let a
|
||||
# user_data tweak silently rebuild and wipe it.
|
||||
ignore_changes = [user_data]
|
||||
}
|
||||
}
|
||||
|
||||
resource "digitalocean_firewall" "services" {
|
||||
name = "services-fw"
|
||||
droplet_ids = [digitalocean_droplet.services.id]
|
||||
|
||||
inbound_rule {
|
||||
protocol = "tcp"
|
||||
port_range = "22"
|
||||
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
# Forgejo HTTP (ct 3000 / mc 3001 / quinn 3002) + git-SSH (2222/2223/2224) + Verdaccio 4873
|
||||
inbound_rule {
|
||||
protocol = "tcp"
|
||||
port_range = "3000-3002"
|
||||
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
inbound_rule {
|
||||
protocol = "tcp"
|
||||
port_range = "2222-2224"
|
||||
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
inbound_rule {
|
||||
protocol = "tcp"
|
||||
port_range = "4873"
|
||||
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
# 80/443 for future Caddy/TLS
|
||||
inbound_rule {
|
||||
protocol = "tcp"
|
||||
port_range = "80"
|
||||
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
inbound_rule {
|
||||
protocol = "tcp"
|
||||
port_range = "443"
|
||||
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
|
||||
outbound_rule {
|
||||
protocol = "tcp"
|
||||
port_range = "1-65535"
|
||||
destination_addresses = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
outbound_rule {
|
||||
protocol = "udp"
|
||||
port_range = "1-65535"
|
||||
destination_addresses = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
outbound_rule {
|
||||
protocol = "icmp"
|
||||
destination_addresses = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
}
|
||||
|
||||
output "services_ip" {
|
||||
value = digitalocean_droplet.services.ipv4_address
|
||||
}
|
||||
|
||||
output "forge_urls" {
|
||||
value = {
|
||||
ct = "http://${digitalocean_droplet.services.ipv4_address}:3000"
|
||||
mc = "http://${digitalocean_droplet.services.ipv4_address}:3001"
|
||||
quinn = "http://${digitalocean_droplet.services.ipv4_address}:3002"
|
||||
npm = "http://${digitalocean_droplet.services.ipv4_address}:4873"
|
||||
}
|
||||
}
|
||||
29
variables.tf
Normal file
29
variables.tf
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
variable "do_token" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "DigitalOcean PAT (ct project)."
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
default = "nyc3"
|
||||
}
|
||||
|
||||
variable "droplet_size" {
|
||||
type = string
|
||||
default = "s-2vcpu-4gb"
|
||||
}
|
||||
|
||||
variable "ssh_key_fingerprints" {
|
||||
type = list(string)
|
||||
description = "DO SSH key fingerprints authorized on the box."
|
||||
default = [
|
||||
"00:b5:2c:23:67:43:e5:39:c9:c2:43:31:6e:5c:03:10", # plum-natalie (operator laptop)
|
||||
"b2:7e:66:b1:9b:61:ac:69:c5:96:a9:97:34:5c:9b:db", # cocotte-fleet
|
||||
]
|
||||
}
|
||||
|
||||
variable "name" {
|
||||
type = string
|
||||
default = "services"
|
||||
}
|
||||
13
versions.tf
Normal file
13
versions.tf
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
terraform {
|
||||
required_version = ">= 1.5"
|
||||
required_providers {
|
||||
digitalocean = {
|
||||
source = "digitalocean/digitalocean"
|
||||
version = "~> 2.40"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "digitalocean" {
|
||||
token = var.do_token
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue