@@ -8,98 +8,100 @@ Single EC2 instance with Docker Compose.
88
99Terraform creates:
1010- VPC with public subnet
11- - EC2 instance (t3.medium, Ubuntu 22.04 LTS)
12- - EBS volume (50 GiB gp3, encrypted)
11+ - EC2 instance (Ubuntu 22.04 LTS)
12+ - EBS volumes (configurable types: gp3, st1, sc1 , encrypted)
1313- Security Group (SSH + Grafana ports)
1414- Elastic IP (optional)
1515
16- On first boot, EC2 instance clones this repository and runs ` docker-compose up ` to start all monitoring services.
16+ On first boot, EC2 instance clones the specified version of this repository and runs ` docker-compose up ` to start all monitoring services.
1717
1818## Quick start
1919
2020See [ QUICKSTART.md] ( QUICKSTART.md ) for step-by-step guide.
2121
22- ## Configuration
22+ ### Validation
2323
24- ### Minimal setup
24+ ``` bash
25+ # Check prerequisites
26+ terraform version
27+ aws sts get-caller-identity
28+
29+ # Validate configuration
30+ terraform init
31+ terraform validate
32+ terraform plan
33+ ```
2534
26- ``` hcl
27- # terraform.tfvars
28- ssh_key_name = "postgres-ai-key"
35+ ## Configuration
2936
30- # Optional: Set custom Grafana password (defaults to 'demo')
31- # grafana_password = "YourSecurePassword123!"
32- ```
37+ ### Required parameters
3338
34- ### Minimal production setup
39+ All parameters in ` terraform.tfvars ` must be explicitly set (uncommented):
3540
3641``` hcl
3742# terraform.tfvars
3843
3944# REQUIRED PARAMETERS
40- ssh_key_name = "your-key-name"
41-
42- # AWS SETTINGS
43- aws_region = "us-east-1"
44- environment = "production"
45- instance_type = "t3.medium"
46-
47- # STORAGE
48- data_volume_size = 50 # GiB
45+ ssh_key_name = "your-key-name"
46+ aws_region = "us-east-1"
47+ environment = "production"
48+ instance_type = "t3.medium"
49+ data_volume_size = 50
50+ data_volume_type = "gp3" # gp3 (SSD), st1 (HDD), sc1 (HDD)
51+ root_volume_type = "gp3"
52+ allowed_ssh_cidr = ["203.0.113.0/24"]
53+ allowed_cidr_blocks = ["203.0.113.0/24"]
54+ use_elastic_ip = true
55+ grafana_password = "YourSecurePassword123!"
56+ ```
4957
50- # SECURITY (restrict access!)
51- allowed_ssh_cidr = ["0.0.0.0/0"] # WARNING: Allows access from anywhere
52- allowed_cidr_blocks = ["0.0.0.0/0"] # WARNING: Allows access from anywhere
58+ ### Optional parameters
5359
54- # OPTIONAL PARAMETERS
55- # grafana_password = "YourSecurePassword123!" # Defaults to 'demo'
56- # postgres_ai_api_key = "your-api-key" # For uploading reports
57- # enable_demo_db = false # true for testing
58- # use_elastic_ip = true # Stable IP address
60+ ``` hcl
61+ # OPTIONAL (have defaults)
62+ postgres_ai_api_key = "your-api-key" # For uploading reports
63+ enable_demo_db = false # Demo database (default: true)
64+ postgres_ai_version = "main" # Git branch/tag (default: "main")
5965
6066monitoring_instances = [
6167 {
62- name = "main-db"
63- conn_str = "postgresql://monitor:pass@db.example.com:5432/postgres"
68+ name = "main-db"
69+ conn_str = "postgresql://monitor:pass@db.example.com:5432/postgres"
6470 environment = "production"
65- cluster = "main"
66- node_name = "primary"
71+ cluster = "main"
72+ node_name = "primary"
6773 }
6874]
6975```
7076
71- ### Full configuration
77+ ### Full example
7278
7379``` hcl
74- # AWS
75- aws_region = "us-east-1"
76- environment = "production"
77- instance_type = "t3.medium"
78-
79- # Storage
80- data_volume_size = 50
81-
82- # Security (restrict access in production)
83- allowed_ssh_cidr = ["203.0.113.0/24"]
84- allowed_cidr_blocks = ["203.0.113.0/24"]
85-
86- # Required
87- ssh_key_name = "ssh-key"
88-
89- # Optional
90- grafana_password = "SecurePassword123!" # Defaults to 'demo'
91- postgres_ai_api_key = "your-api-key"
92- enable_demo_db = false
93- use_elastic_ip = true
80+ # REQUIRED
81+ ssh_key_name = "postgres-ai-key"
82+ aws_region = "us-east-1"
83+ environment = "production"
84+ instance_type = "t3.medium"
85+ data_volume_size = 100
86+ data_volume_type = "gp3"
87+ root_volume_type = "gp3"
88+ allowed_ssh_cidr = ["203.0.113.0/24"]
89+ allowed_cidr_blocks = ["203.0.113.0/24"]
90+ use_elastic_ip = true
91+ grafana_password = "SecurePassword123!"
92+
93+ # OPTIONAL
94+ postgres_ai_api_key = "your-api-key"
95+ enable_demo_db = false
96+ postgres_ai_version = "v0.9"
9497
95- # Monitoring instances
9698monitoring_instances = [
9799 {
98- name = "prod-db"
99- conn_str = "postgresql://monitor:pass@db.example.com:5432/postgres"
100+ name = "prod-db"
101+ conn_str = "postgresql://monitor:pass@db.example.com:5432/postgres"
100102 environment = "production"
101- cluster = "main"
102- node_name = "primary"
103+ cluster = "main"
104+ node_name = "primary"
103105 }
104106]
105107```
@@ -111,7 +113,7 @@ monitoring_instances = [
111113``` bash
112114terraform output ssh_command
113115# Or directly:
114- ssh -i ~ /.ssh/postgres-ai-key.pem ubuntu@$( terraform output -raw public_ip )
116+ ssh -i ~ /.ssh/postgres-ai-key.pem ubuntu@$( terraform output -raw external_ip )
115117```
116118
117119### Service management
@@ -132,14 +134,19 @@ sudo systemctl restart postgres-ai
132134
133135### Add monitoring instance
134136
135- Method 1: Update terraform.tfvars and run ` terraform apply `
137+ Method 1: Update ` terraform.tfvars ` and apply changes:
138+ ``` bash
139+ # Edit terraform.tfvars, add to monitoring_instances array
140+ terraform apply
141+ # Automatically updates instances.yml and restarts pgwatch services
142+ ```
136143
137- Method 2: Manual configuration on server :
144+ Method 2: Manual configuration (avoids credentials in state) :
138145``` bash
139146ssh ubuntu@your-ip
140147cd /home/postgres_ai/postgres_ai
141148sudo -u postgres_ai vim instances.yml
142- sudo docker-compose restart
149+ sudo -u postgres_ai ./postgres_ai update-config
143150```
144151
145152### Backup
@@ -247,16 +254,16 @@ ssh ubuntu@your-ip "sudo resize2fs /dev/nvme1n1"
247254Choose instance type based on monitoring workload:
248255
249256``` hcl
250- instance_type = "t3.medium " # 2 vCPU, 4 GiB RAM
257+ instance_type = "t3.small " # 2 vCPU, 2 GiB RAM
251258```
252259
253260Suitable for:
254- - Monitoring 1-3 small databases
261+ - Monitoring 1-2 small databases
255262- Dev/test environments
256263- Proof of concept
257264
258265``` hcl
259- instance_type = "t3.medium" # 2 vCPU, 8 GiB RAM (default)
266+ instance_type = "t3.medium" # 2 vCPU, 4 GiB RAM
260267```
261268
262269Suitable for:
@@ -271,6 +278,28 @@ Suitable for:
271278- Monitoring 10+ databases
272279- High-frequency metric collection
273280
281+ ## Storage options
282+
283+ ### Volume types
284+
285+ ``` hcl
286+ # SSD (recommended for production)
287+ data_volume_type = "gp3" # General Purpose SSD
288+ root_volume_type = "gp3"
289+
290+ # HDD (lower cost for testing)
291+ data_volume_type = "st1" # Throughput Optimized HDD (min 125 GiB)
292+ data_volume_type = "sc1" # Cold HDD (min 125 GiB)
293+ ```
294+
295+ ### Volume sizing
296+
297+ ``` hcl
298+ data_volume_size = 50 # Small deployments
299+ data_volume_size = 100 # Medium deployments
300+ data_volume_size = 500 # Large deployments
301+ ```
302+
274303## Custom domain
275304
276305``` bash
@@ -284,7 +313,7 @@ aws route53 change-resource-record-sets \
284313 "Name": "monitoring.example.com",
285314 "Type": "A",
286315 "TTL": 300,
287- "ResourceRecords": [{"Value": "' " $( terraform output -raw public_ip ) " ' "}]
316+ "ResourceRecords": [{"Value": "' " $( terraform output -raw external_ip ) " ' "}]
288317 }
289318 }]
290319 }'
@@ -315,3 +344,30 @@ This deployment is appropriate for:
315344- Teams with Linux administration skills
316345
317346For production-critical systems requiring high availability, consider managed services (RDS, ECS Fargate) instead.
347+
348+ ## Security considerations
349+
350+ ### Credentials in Terraform state
351+
352+ All credentials (passwords, connection strings) are stored in plain text in ` terraform.tfstate ` . This is acceptable for:
353+ - Development and testing
354+ - One-off monitoring deployments
355+ - When state files are properly secured
356+
357+ For production deployments:
358+ - Use remote state with encryption (S3 + KMS)
359+ - Use environment variables: ` export TF_VAR_grafana_password=... `
360+ - Configure monitoring instances manually after deployment (Method 2)
361+ - Store state in private repositories only
362+
363+ ### IMDSv2
364+
365+ EC2 instances are configured to require IMDSv2 (Instance Metadata Service v2) for enhanced security against SSRF attacks.
366+
367+ ### Cleanup
368+
369+ After destroying infrastructure, remove local state files:
370+ ``` bash
371+ terraform destroy
372+ rm -rf .terraform/ terraform.tfstate*
373+ ```
0 commit comments