Files
bible/infra/railway/main.tf
T
2026-09-19 13:10:34 -04:00

128 lines
3.2 KiB
Terraform

# The site on Railway: one project, with an environment each for prod and dev,
# and one service that runs the repo's Dockerfile. server.py does the routing,
# as it does locally; Railway holds the certificates.
#
# Terraform makes the project, the service and the domains, and puts their
# records on Cloudflare. It never deploys: deploy.sh uploads the site with
# Railway's CLI. State is local (terraform.tfstate here, gitignored).
terraform {
required_version = ">= 1.10"
required_providers {
railway = { source = "terraform-community-providers/railway", version = "~> 0.6.2" }
cloudflare = { source = "cloudflare/cloudflare", version = "~> 5.25" }
}
}
# Reads RAILWAY_TOKEN (compose.yaml sets it from RAILWAY_API_TOKEN in .env):
# a workspace token.
provider "railway" {}
# Reads CLOUDFLARE_API_TOKEN: DNS edit and zone read, on aerlinn.net only.
provider "cloudflare" {}
locals {
zone = "aerlinn.net"
# Railway Metal, Virginia.
region = "us-east4-eqdc4a"
}
resource "railway_project" "bible" {
name = "bible"
description = "Offline Bible reader"
default_environment = {
name = "production"
}
}
resource "railway_environment" "dev" {
name = "dev"
project_id = railway_project.bible.id
}
resource "railway_service" "site" {
name = "site"
project_id = railway_project.bible.id
regions = [{
region = local.region
num_replicas = 1
}]
# Made after both environments, so it is in both.
depends_on = [railway_environment.dev]
}
locals {
environments = {
production = {
id = railway_project.bible.default_environment.id
domain = "bible-2.${local.zone}"
}
dev = {
id = railway_environment.dev.id
domain = "bible-dev.${local.zone}"
}
}
}
resource "railway_custom_domain" "site" {
for_each = local.environments
domain = each.value.domain
environment_id = each.value.id
service_id = railway_service.site.id
}
data "cloudflare_zone" "site" {
filter = {
name = local.zone
}
}
# DNS only, not proxied: Railway issues the certificate itself and has to see
# the CNAME as it is.
resource "cloudflare_dns_record" "site" {
for_each = railway_custom_domain.site
zone_id = data.cloudflare_zone.site.id
name = each.value.domain
type = "CNAME"
content = each.value.dns_record_value
ttl = 1
proxied = false
comment = "terraform: railway bible ${each.key}"
}
# Railway's proof that the domain is ours.
resource "cloudflare_dns_record" "verification" {
for_each = railway_custom_domain.site
zone_id = data.cloudflare_zone.site.id
name = "${each.value.verification_host_label}.${each.value.zone}"
type = "TXT"
content = "\"${each.value.verification_record_value}\""
ttl = 1
comment = "terraform: railway bible ${each.key} verification"
}
output "project_id" {
value = railway_project.bible.id
}
output "service" {
value = railway_service.site.name
}
output "domains" {
value = {
for name, d in railway_custom_domain.site : name => {
domain = d.domain
host_label = d.host_label
zone = d.zone
target = d.dns_record_value
verification_host = d.verification_host_label
verification = d.verification_record_value
}
}
}