made aws infra
Some checks are pending
Deploy Home Assistant / deploy (push) Waiting to run
Build juntekim.com / Push-to-juntekim-to-docker-hub (push) Waiting to run
Build juntekim.com / run-on-k8s (push) Blocked by required conditions
Deploy n8n / deploy (push) Waiting to run
Build & Deploy stripe-to-invoice (with DB secrets + migrations) / build (push) Waiting to run
Build & Deploy stripe-to-invoice (with DB secrets + migrations) / Deploy Postgres (PV + PVC + Deployment) (push) Blocked by required conditions
Build & Deploy stripe-to-invoice (with DB secrets + migrations) / Apply runtime secrets (push) Blocked by required conditions
Build & Deploy stripe-to-invoice (with DB secrets + migrations) / Run DB migrations (Atlas) (push) Blocked by required conditions
Build & Deploy stripe-to-invoice (with DB secrets + migrations) / deploy (push) Blocked by required conditions
Terraform Apply / Terraform Apply (push) Waiting to run
Terraform Apply / Terraform Apply - SES (push) Blocked by required conditions

This commit is contained in:
Jun-te Kim 2026-03-12 05:56:46 +00:00
parent 45f2c5057c
commit 97bd6854f4
11 changed files with 174 additions and 10 deletions

View file

@ -1,16 +1,9 @@
## AWS S3 + Terraform Setup (TODO - do first)
- [ ] Create new S3 bucket for Terraform state (e.g. `juntekim-terraform-state`)
- [ ] Enable versioning on the bucket
- [ ] Set up Terraform with S3 backend pointing to new bucket
- [ ] Use Terraform to define new infra (start with what exists, then expand)
- [ ] Store all future infra changes via version-controlled Terraform in Forgejo
## Forgejo Backup (TODO)
- [ ] Set up restic CronJob to back up forgejo-pvc (/data) to S3
- [ ] Set up restic CronJob to back up forgejo-pvc (/data) to S3 weekly
- Mount forgejo-pvc read-only in CronJob
- Use restic to snapshot to S3 bucket (need: S3_BUCKET, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, RESTIC_PASSWORD)
- Use restic to snapshot to S3 bucket and put it under repos (need: S3_BUCKET, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, RESTIC_PASSWORD)
- Schedule: daily
- [ ] Forgejo postgres backup via databasus (separate)
- [ ] Forgejo postgres backup via databasus same bucket but key would be postgres
- [ ] Test restore from restic snapshot up
# Lets deploy databasus first since i'm using it to sort my back up strategy first

4
aws_infra/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
.terraform/
.terraform.lock.hcl
*.tfstate
*.tfstate.backup

7
aws_infra/main.tf Normal file
View file

@ -0,0 +1,7 @@
# aws_infra central infrastructure managed via juntekim-infra-state
module "forgejo_backup" {
source = "./modules/forgejo_backup"
bucket_name = "juntekim-forgejo-backup"
}

View file

@ -0,0 +1,39 @@
module "bucket" {
source = "../s3_bucket"
bucket_name = var.bucket_name
versioning_enabled = true
retention_days = 90
}
resource "aws_iam_user" "forgejo_backup" {
name = "forgejo-backup"
}
resource "aws_iam_access_key" "forgejo_backup" {
user = aws_iam_user.forgejo_backup.name
}
resource "aws_iam_user_policy" "forgejo_backup" {
name = "forgejo-backup-s3"
user = aws_iam_user.forgejo_backup.name
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:ListBucket"
]
Resource = [
module.bucket.bucket_arn,
"${module.bucket.bucket_arn}/*"
]
}
]
})
}

View file

@ -0,0 +1,16 @@
output "bucket_name" {
value = module.bucket.bucket_name
}
output "bucket_arn" {
value = module.bucket.bucket_arn
}
output "iam_access_key_id" {
value = aws_iam_access_key.forgejo_backup.id
}
output "iam_secret_access_key" {
value = aws_iam_access_key.forgejo_backup.secret
sensitive = true
}

View file

@ -0,0 +1,5 @@
variable "bucket_name" {
description = "Name of the S3 bucket for Forgejo backups"
type = string
default = "juntekim-forgejo-backup"
}

View file

@ -0,0 +1,51 @@
resource "aws_s3_bucket" "this" {
bucket = var.bucket_name
}
resource "aws_s3_bucket_versioning" "this" {
bucket = aws_s3_bucket.this.id
versioning_configuration {
status = var.versioning_enabled ? "Enabled" : "Disabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
bucket = aws_s3_bucket.this.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_public_access_block" "this" {
bucket = aws_s3_bucket.this.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_lifecycle_configuration" "this" {
count = var.retention_days > 0 ? 1 : 0
bucket = aws_s3_bucket.this.id
rule {
id = "expire-objects"
status = "Enabled"
filter {}
transition {
days = 30
storage_class = "STANDARD_IA"
}
expiration {
days = var.retention_days
}
}
}

View file

@ -0,0 +1,7 @@
output "bucket_name" {
value = aws_s3_bucket.this.bucket
}
output "bucket_arn" {
value = aws_s3_bucket.this.arn
}

View file

@ -0,0 +1,16 @@
variable "bucket_name" {
description = "Name of the S3 bucket"
type = string
}
variable "versioning_enabled" {
description = "Enable versioning on the bucket"
type = bool
default = true
}
variable "retention_days" {
description = "Number of days before objects expire. 0 disables expiry."
type = number
default = 0
}

21
aws_infra/provider.tf Normal file
View file

@ -0,0 +1,21 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "juntekim-infra-state"
key = "aws_infra/terraform.tfstate"
region = "eu-west-2"
profile = "personal"
}
required_version = ">= 1.5.0"
}
provider "aws" {
region = var.aws_region
}

5
aws_infra/variables.tf Normal file
View file

@ -0,0 +1,5 @@
variable "aws_region" {
description = "AWS region"
type = string
default = "eu-west-2"
}