This Tofu / Terraform repo builds an a single AWS instance for my containerised platform running in a single Spot instance. AWS can stop it, delete it, whatever - because the user_data sets everything up from scratch and any important data is kept on an external volume.
It basically looks like this:
-
A dedicated VPC, and EC2 Spot instance spun up with Terraform / (OpenTofu) running a Debian Sid AMI that I've encrypted with a customer managed key.
-
The root volume is small (8G) and remains mostly untouched with only enough changes to the root volume to enable it to reboot without needing any configuration changes.
-
All important persistent data and configuration lives on a separate encrypted volume mounted at /volume. This terraform project connects the instance to it, but doesn't manage it directly.
-
Everything important is running as a Docker container via Docker Compose. There are 5 major Docker containers that need to remain up:
- certbot: Mostly sleeping for 12 hours at a time but then checking for certs that need to be renewed
- nginx: Powers all the static and tool sites.
- php: Has the same mounts as nginx and runs any PHP needed. I self compile this as PHP's a monstrocity.
- mariadb: Powers any needed mysql/mariadb databases.
- gitea: A place I intend to move most of my private repos into. Kind of like Gitlab, but not as bloated with crap.
-
I also have a fully monitoring / metrics / observability stack running:
- prometheus: The metrics engine.
- grafana : A virtual pane of glass to view all the metrics.
- cadvisor: Analyzes resource usage and performance characteristics of running containers.
- node-exporter : Scrapes and exports my instance's metrics.
-
All Powering These Sites:
- My home page, a static site built every 5 minutes from Git via Hugo.
- A Wordpress site powering a personal archive. (nginx and php)
- bocan.dev - A 1 page CV site. (just nginx)
- cfunder.me - A personal URL shortener. (nginx and php)
- My personal blog (nginx and hugo), and tooling hidden underneath:
- A personal photo gallery powered by Piwigo.
- A webapp that gives quotes and advice.
- My self hosted Nextcloud engine.
- A time dashboard I built to amuse myself.
- My business site (just nginx) - but soon to be my business blog (nginx and hugo)
- My family tree site (just nginx)
-
There are 3 crontab jobs executing commands inside the docker containers:
-
Issues I still need to fix:
- I'd like to swap the AMI and disk encryption to use a CMK.
- The Github repo that controls all of it stores the web certificates so I can't make it public yet. I need to split that piece out.
- The big external volume only has 1 snapshot and it's not automated yet.
Name | Version |
---|---|
terraform | >= 1.8.0 |
aws | >= 5.0 |
Name | Version |
---|---|
aws | >= 5.0 |
Name | Source | Version |
---|---|---|
ec2_instance | terraform-aws-modules/ec2-instance/aws | 5.7.0 |
ec2_instance_freyja | terraform-aws-modules/ec2-instance/aws | 5.7.0 |
kms | terraform-aws-modules/kms/aws | 3.1.0 |
security_group | terraform-aws-modules/security-group/aws | 5.2.0 |
security_group_freyja | terraform-aws-modules/security-group/aws | 5.2.0 |
vpc | terraform-aws-modules/vpc/aws | 5.13.0 |
Name | Type |
---|---|
aws_ami_copy.debian_encrypted_ami | resource |
aws_dlm_lifecycle_policy.odin_dlm_policy | resource |
aws_dlm_lifecycle_policy.odin_dlm_policy_monthly | resource |
aws_eip.bar | resource |
aws_eip.foo | resource |
aws_iam_instance_profile.ec2_profile | resource |
aws_iam_policy.ec2_policy | resource |
aws_iam_role.dlm_lifecycle_role | resource |
aws_iam_role.ec2_role | resource |
aws_iam_role_policy.dlm_lifecycle | resource |
aws_iam_role_policy_attachment.custom | resource |
aws_route53_record.mailserverA | resource |
aws_volume_attachment.this | resource |
aws_volume_attachment.this2 | resource |
aws_ami.debian | data source |
aws_ami.encrypted-ami | data source |
aws_availability_zones.available | data source |
aws_caller_identity.current | data source |
aws_ebs_volume.ebs_volume | data source |
aws_ebs_volume.ebs_volume_freyja | data source |
aws_iam_policy_document.assume_role | data source |
aws_iam_policy_document.dlm_lifecycle | data source |
Name | Description | Type | Default | Required |
---|---|---|---|---|
ami_override | The Debian Sid AMI can be updated too fast. Set this if you don't want to update it. | string |
null |
no |
github_token | The github token I use to let Hugo write back to Github. | string |
n/a | yes |
github_user | The github user I use to let Hugo write back to Github. | string |
n/a | yes |
passphrase | Password to encrypt state | string |
n/a | yes |
users_for_key | The users or sts roles to give access to the customer managed key | list(string) |
null |
no |
Name | Description |
---|---|
ami_details | n/a |
cgw_arns | List of ARNs of Customer Gateway |
cgw_ids | List of IDs of Customer Gateway |
database_internet_gateway_route_id | ID of the database internet gateway route |
database_ipv6_egress_route_id | ID of the database IPv6 egress route |
database_nat_gateway_route_ids | List of IDs of the database nat gateway route |
database_network_acl_arn | ARN of the database network ACL |
database_network_acl_id | ID of the database network ACL |
database_route_table_association_ids | List of IDs of the database route table association |
database_route_table_ids | List of IDs of database route tables |
database_subnet_arns | List of ARNs of database subnets |
database_subnet_group | ID of database subnet group |
database_subnet_group_name | Name of database subnet group |
database_subnets | List of IDs of database subnets |
database_subnets_cidr_blocks | List of cidr_blocks of database subnets |
database_subnets_ipv6_cidr_blocks | List of IPv6 cidr_blocks of database subnets in an IPv6 enabled VPC |
default_network_acl_id | The ID of the default network ACL |
default_route_table_id | The ID of the default route table |
default_security_group_id | The ID of the security group created by default on VPC creation |
default_vpc_arn | The ARN of the Default VPC |
default_vpc_cidr_block | The CIDR block of the Default VPC |
default_vpc_default_network_acl_id | The ID of the default network ACL of the Default VPC |
default_vpc_default_route_table_id | The ID of the default route table of the Default VPC |
default_vpc_default_security_group_id | The ID of the security group created by default on Default VPC creation |
default_vpc_enable_dns_hostnames | Whether or not the Default VPC has DNS hostname support |
default_vpc_enable_dns_support | Whether or not the Default VPC has DNS support |
default_vpc_id | The ID of the Default VPC |
default_vpc_instance_tenancy | Tenancy of instances spin up within Default VPC |
default_vpc_main_route_table_id | The ID of the main route table associated with the Default VPC |
dhcp_options_id | The ID of the DHCP options |
ec2_instance_arn | The ARN of the instance |
ec2_instance_availability_zone | The availability zone of the created instance |
ec2_instance_capacity_reservation_specification | Capacity reservation specification of the instance |
ec2_instance_ebs_block_device | EBS block device information |
ec2_instance_ephemeral_block_device | Ephemeral block device information |
ec2_instance_iam_instance_profile_arn | ARN assigned by AWS to the instance profile |
ec2_instance_iam_instance_profile_id | Instance profile's ID |
ec2_instance_iam_instance_profile_unique | Stable and unique string identifying the IAM instance profile |
ec2_instance_iam_role_arn | The Amazon Resource Name (ARN) specifying the IAM role |
ec2_instance_iam_role_name | The name of the IAM role |
ec2_instance_iam_role_unique_id | Stable and unique string identifying the IAM role |
ec2_instance_id | The ID of the instance |
ec2_instance_instance_state | The state of the instance. One of: pending , running , shutting-down , terminated , stopping , stopped |
ec2_instance_primary_network_interface_id | The ID of the instance's primary network interface |
ec2_instance_private_dns | The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC |
ec2_instance_public_dns | The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC |
ec2_instance_public_ip | The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use public_ip as this field will change after the EIP is attached |
ec2_instance_root_block_device | Root block device information |
ec2_instance_tags_all | A map of tags assigned to the resource, including those inherited from the provider default_tags configuration block |
egress_only_internet_gateway_id | The ID of the egress only Internet Gateway |
elasticache_network_acl_arn | ARN of the elasticache network ACL |
elasticache_network_acl_id | ID of the elasticache network ACL |
elasticache_route_table_association_ids | List of IDs of the elasticache route table association |
elasticache_route_table_ids | List of IDs of elasticache route tables |
elasticache_subnet_arns | List of ARNs of elasticache subnets |
elasticache_subnet_group | ID of elasticache subnet group |
elasticache_subnet_group_name | Name of elasticache subnet group |
elasticache_subnets | List of IDs of elasticache subnets |
elasticache_subnets_cidr_blocks | List of cidr_blocks of elasticache subnets |
elasticache_subnets_ipv6_cidr_blocks | List of IPv6 cidr_blocks of elasticache subnets in an IPv6 enabled VPC |
igw_arn | The ARN of the Internet Gateway |
igw_id | The ID of the Internet Gateway |
intra_network_acl_arn | ARN of the intra network ACL |
intra_network_acl_id | ID of the intra network ACL |
intra_route_table_association_ids | List of IDs of the intra route table association |
intra_route_table_ids | List of IDs of intra route tables |
intra_subnet_arns | List of ARNs of intra subnets |
intra_subnets | List of IDs of intra subnets |
intra_subnets_cidr_blocks | List of cidr_blocks of intra subnets |
intra_subnets_ipv6_cidr_blocks | List of IPv6 cidr_blocks of intra subnets in an IPv6 enabled VPC |
nat_ids | List of allocation ID of Elastic IPs created for AWS NAT Gateway |
nat_public_ips | List of public Elastic IPs created for AWS NAT Gateway |
natgw_ids | List of NAT Gateway IDs |
outpost_network_acl_arn | ARN of the outpost network ACL |
outpost_network_acl_id | ID of the outpost network ACL |
outpost_subnet_arns | List of ARNs of outpost subnets |
outpost_subnets | List of IDs of outpost subnets |
outpost_subnets_cidr_blocks | List of cidr_blocks of outpost subnets |
outpost_subnets_ipv6_cidr_blocks | List of IPv6 cidr_blocks of outpost subnets in an IPv6 enabled VPC |
private_ipv6_egress_route_ids | List of IDs of the ipv6 egress route |
private_nat_gateway_route_ids | List of IDs of the private nat gateway route |
private_network_acl_arn | ARN of the private network ACL |
private_network_acl_id | ID of the private network ACL |
private_route_table_association_ids | List of IDs of the private route table association |
private_route_table_ids | List of IDs of private route tables |
private_subnet_arns | List of ARNs of private subnets |
private_subnets | List of IDs of private subnets |
private_subnets_cidr_blocks | List of cidr_blocks of private subnets |
private_subnets_ipv6_cidr_blocks | List of IPv6 cidr_blocks of private subnets in an IPv6 enabled VPC |
ptr_record | public dns |
public_internet_gateway_ipv6_route_id | ID of the IPv6 internet gateway route |
public_internet_gateway_route_id | ID of the internet gateway route |
public_network_acl_arn | ARN of the public network ACL |
public_network_acl_id | ID of the public network ACL |
public_route_table_association_ids | List of IDs of the public route table association |
public_route_table_ids | List of IDs of public route tables |
public_subnet_arns | List of ARNs of public subnets |
public_subnets | List of IDs of public subnets |
public_subnets_cidr_blocks | List of cidr_blocks of public subnets |
public_subnets_ipv6_cidr_blocks | List of IPv6 cidr_blocks of public subnets in an IPv6 enabled VPC |
redshift_network_acl_arn | ARN of the redshift network ACL |
redshift_network_acl_id | ID of the redshift network ACL |
redshift_public_route_table_association_ids | List of IDs of the public redshift route table association |
redshift_route_table_association_ids | List of IDs of the redshift route table association |
redshift_route_table_ids | List of IDs of redshift route tables |
redshift_subnet_arns | List of ARNs of redshift subnets |
redshift_subnet_group | ID of redshift subnet group |
redshift_subnets | List of IDs of redshift subnets |
redshift_subnets_cidr_blocks | List of cidr_blocks of redshift subnets |
redshift_subnets_ipv6_cidr_blocks | List of IPv6 cidr_blocks of redshift subnets in an IPv6 enabled VPC |
this_customer_gateway | Map of Customer Gateway attributes |
vgw_arn | The ARN of the VPN Gateway |
vgw_id | The ID of the VPN Gateway |
vpc_arn | The ARN of the VPC |
vpc_cidr_block | The CIDR block of the VPC |
vpc_enable_dns_hostnames | Whether or not the VPC has DNS hostname support |
vpc_enable_dns_support | Whether or not the VPC has DNS support |
vpc_flow_log_cloudwatch_iam_role_arn | The ARN of the IAM role used when pushing logs to Cloudwatch log group |
vpc_flow_log_destination_arn | The ARN of the destination for VPC Flow Logs |
vpc_flow_log_destination_type | The type of the destination for VPC Flow Logs |
vpc_flow_log_id | The ID of the Flow Log resource |
vpc_id | The ID of the VPC |
vpc_instance_tenancy | Tenancy of instances spin up within VPC |
vpc_ipv6_association_id | The association ID for the IPv6 CIDR block |
vpc_ipv6_cidr_block | The IPv6 CIDR block |
vpc_main_route_table_id | The ID of the main route table associated with this VPC |
vpc_owner_id | The ID of the AWS account that owns the VPC |
vpc_secondary_cidr_blocks | List of secondary CIDR blocks of the VPC |