diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index 79b903c..4d4a07c 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -126,7 +126,11 @@
4. Studente comprende il parallelismo tra container con limiti e EC2 instances con instance types
5. Lab include test che verificano resource limits con `docker stats` e healthcheck readiness
-**Plans:** TBD
+**Plans:** 3
+
+- [ ] [04-01-PLAN.md](.planning/phases/04-lab-03-compute-ec2/04-01-PLAN.md) — Create test infrastructure (Wave 0: resource limits tests, healthcheck tests, enforcement tests, final verification)
+- [ ] [04-02-PLAN.md](.planning/phases/04-lab-03-compute-ec2/04-02-PLAN.md) — Create Diátaxis documentation (Tutorial: 3 parts, How-to: 4 guides, Reference: 3 docs, Explanation: EC2 parallels)
+- [ ] [04-03-PLAN.md](.planning/phases/04-lab-03-compute-ec2/04-03-PLAN.md) — Create infrastructure (docker-compose.yml with resource limits, healthchecks, Dockerfile, infrastructure verification)
---
diff --git a/.planning/phases/04-lab-03-compute-ec2/04-01-PLAN.md b/.planning/phases/04-lab-03-compute-ec2/04-01-PLAN.md
new file mode 100644
index 0000000..68413c8
--- /dev/null
+++ b/.planning/phases/04-lab-03-compute-ec2/04-01-PLAN.md
@@ -0,0 +1,228 @@
+---
+phase: 04-lab-03-compute-ec2
+plan: 01
+type: execute
+wave: 0
+depends_on: []
+files_modified:
+ - labs/lab-03-compute/tests/01-resource-limits-test.sh
+ - labs/lab-03-compute/tests/02-healthcheck-test.sh
+ - labs/lab-03-compute/tests/03-enforcement-test.sh
+ - labs/lab-03-compute/tests/99-final-verification.sh
+ - labs/lab-03-compute/tests/run-all-tests.sh
+ - labs/lab-03-compute/tests/quick-test.sh
+autonomous: true
+requirements:
+ - TEST-01
+ - TEST-05
+ - INF-03
+ - LAB-03
+user_setup: []
+
+must_haves:
+ truths:
+ - "Test scripts exist and validate resource limits before implementation"
+ - "Tests verify INF-03 compliance (all containers have CPU/memory limits)"
+ - "Tests verify healthcheck implementation"
+ - "Tests can enforce resource limits and verify with docker stats"
+ - "Final verification script provides clear pass/fail report"
+ artifacts:
+ - path: "labs/lab-03-compute/tests/01-resource-limits-test.sh"
+ provides: "Resource limits validation"
+ min_lines: 80
+ - path: "labs/lab-03-compute/tests/02-healthcheck-test.sh"
+ provides: "Healthcheck testing"
+ min_lines: 100
+ - path: "labs/lab-03-compute/tests/03-enforcement-test.sh"
+ provides: "Resource enforcement verification"
+ min_lines: 120
+ - path: "labs/lab-03-compute/tests/99-final-verification.sh"
+ provides: "Student double-check command"
+ min_lines: 100
+ - path: "labs/lab-03-compute/tests/run-all-tests.sh"
+ provides: "Test orchestration with fail-fast"
+ min_lines: 50
+ - path: "labs/lab-03-compute/tests/quick-test.sh"
+ provides: "Quick validation for development"
+ min_lines: 30
+ key_links:
+ - from: "tests/01-resource-limits-test.sh"
+ to: "docker-compose.yml resources"
+ via: "yq or grep for deploy.resources.limits"
+ pattern: "deploy:.*resources:.*limits"
+ - from: "tests/02-healthcheck-test.sh"
+ to: "docker-compose.yml healthcheck"
+ via: "grep for healthcheck section"
+ pattern: "healthcheck:"
+ - from: "tests/03-enforcement-test.sh"
+ to: "docker stats"
+ via: "docker stats --no-stream for verification"
+ pattern: "docker.*stats"
+ - from: "tests/99-final-verification.sh"
+ to: "INF-03 requirement"
+ via: "Verify all services have cpu and memory limits"
+ pattern: "INF-03"
+---
+
+
+Create comprehensive test infrastructure for Lab 03 (Compute & EC2) following TDD RED phase methodology. Tests validate Docker Compose resource limits (CPU/memory), healthcheck implementation, and INF-03 compliance (all containers must have resource limits).
+
+Purpose: Establish verification foundation before implementing compute infrastructure. Tests fail initially (RED phase) and pass after implementation (GREEN phase in Plan 04-03).
+
+Output: 6 bash test scripts covering resource limits validation, healthcheck testing, enforcement verification, and final verification for students.
+
+
+
+@/home/luca/.claude/get-shit-done/workflows/execute-plan.md
+@/home/luca/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/REQUIREMENTS.md
+@.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md
+@.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md
+@.planning/phases/03-lab-02-network-vpc/03-01-PLAN.md
+
+# Test Patterns from Phase 2 and 3
+
+From labs/lab-01-iam/tests/run-all-tests.sh:
+- Use `set -euo pipefail` for error handling
+- Color-coded output (GREEN for pass, RED for fail, BLUE for info)
+- Summary with pass/fail counts
+- Exit code 0 for all pass, 1 for any failure
+
+From labs/lab-02-network/tests/04-verify-infrastructure.sh:
+- Parse docker-compose.yml with `docker compose config`
+- Use jq for JSON parsing when needed
+- Verify compliance with grep patterns
+- Use awk for robust counting
+
+# Key Docker Commands for Testing
+
+## Resource Limits Verification
+```bash
+# Check if service has CPU limit
+docker compose config | grep -A 10 "service_name:" | grep -c "cpus:"
+
+# Check if service has memory limit
+docker compose config | grep -A 10 "service_name:" | grep -c "memory:"
+
+# Get container limits
+docker inspect container_name --format '{{.HostConfig.NanoCpus}}'
+docker inspect container_name --format '{{.HostConfig.Memory}}'
+```
+
+## Healthcheck Verification
+```bash
+# Check if service has healthcheck
+docker compose config | grep -A 20 "service_name:" | grep -c "healthcheck:"
+
+# Get container health status
+docker inspect container_name --format '{{.State.Health.Status}}'
+```
+
+## Resource Monitoring
+```bash
+# Get live stats
+docker stats --no-stream
+
+# Get specific container stats
+docker stats container_name --no-stream --format "{{.CPUPerc}}\t{{.MemUsage}}"
+```
+
+# Testing Strategy
+
+## Test 1: Resource Limits Configuration
+**Purpose:** Verify docker-compose.yml has resource limits defined
+**Expected Result:** FAIL initially (no limits configured)
+**Pass Criteria After Implementation:** All services have cpus and memory limits
+
+## Test 2: Healthcheck Configuration
+**Purpose:** Verify services have healthchecks defined
+**Expected Result:** FAIL initially (no healthchecks configured)
+**Pass Criteria After Implementation:** All services have valid healthchecks
+
+## Test 3: Resource Enforcement
+**Purpose:** Deploy test container and verify limits are enforced
+**Expected Result:** FAIL initially (no test container defined)
+**Pass Criteria After Implementation:** docker stats shows enforcement
+
+## Test 4: INF-03 Compliance
+**Purpose:** Verify mandatory resource limits for all services
+**Expected Result:** FAIL initially
+**Pass Criteria After Implementation:** 100% compliance
+
+# Implementation Notes
+
+1. **Directory Structure:**
+```
+labs/lab-03-compute/
+├── tests/
+│ ├── 01-resource-limits-test.sh
+│ ├── 02-healthcheck-test.sh
+│ ├── 03-enforcement-test.sh
+│ ├── 99-final-verification.sh
+│ ├── run-all-tests.sh
+│ └── quick-test.sh
+├── docker-compose.yml (created in 04-03)
+└── README.md
+```
+
+2. **Test Execution Order:**
+ - 01-resource-limits-test.sh: Parse compose file for limits
+ - 02-healthcheck-test.sh: Parse compose file for healthchecks
+ - 03-enforcement-test.sh: Deploy test and verify enforcement
+ - 99-final-verification.sh: End-to-end student verification
+
+3. **Error Handling:**
+ - Each test should be independent
+ - Use descriptive error messages
+ - Provide remediation hints
+
+4. **Color Coding:**
+ ```bash
+ RED='\033[0;31m'
+ GREEN='\033[0;32m'
+ BLUE='\033[0;34m'
+ BOLD='\033[1m'
+ NC='\033[0m'
+ ```
+
+# Success Criteria
+
+Plan 04-01 is complete when:
+1. All 6 test scripts created
+2. Each script meets minimum line requirements
+3. Tests fail when executed on empty/non-existent lab-03-compute
+4. run-all-tests.sh executes all tests in sequence
+5. Tests cover: resource limits, healthchecks, enforcement, INF-03
+
+
+
+1. Create labs/lab-03-compute/tests/ directory
+2. Create 01-resource-limits-test.sh (80+ lines)
+ - Parse docker-compose.yml for deploy.resources.limits
+ - Verify cpus and memory for all services
+ - Report missing limits
+3. Create 02-healthcheck-test.sh (100+ lines)
+ - Parse docker-compose.yml for healthcheck sections
+ - Verify test, interval, timeout, retries
+ - Report missing healthchecks
+4. Create 03-enforcement-test.sh (120+ lines)
+ - Deploy test container with stress image
+ - Verify CPU limit enforcement with docker stats
+ - Verify memory OOM on exceed
+ - Cleanup test container
+5. Create 99-final-verification.sh (100+ lines)
+ - Combine all checks into student-facing verification
+ - INF-03 compliance report
+ - Healthcheck status report
+ - Clear pass/fail summary
+6. Create run-all-tests.sh (50+ lines)
+ - Execute all test scripts in sequence
+ - Fail-fast on first failure
+ - Summary report
+7. Create quick-test.sh (30+ lines)
+ - Fast validation (< 30 seconds)
+ - Essential checks only
+
diff --git a/.planning/phases/04-lab-03-compute-ec2/04-02-PLAN.md b/.planning/phases/04-lab-03-compute-ec2/04-02-PLAN.md
new file mode 100644
index 0000000..3a6198b
--- /dev/null
+++ b/.planning/phases/04-lab-03-compute-ec2/04-02-PLAN.md
@@ -0,0 +1,342 @@
+---
+phase: 04-lab-03-compute-ec2
+plan: 02
+type: execute
+wave: 1
+depends_on:
+ - "04-01"
+files_modified:
+ - labs/lab-03-compute/tutorial/01-set-resource-limits.md
+ - labs/lab-03-compute/tutorial/02-implement-healthchecks.md
+ - labs/lab-03-compute/tutorial/03-dependencies-with-health.md
+ - labs/lab-03-compute/how-to-guides/check-resource-usage.md
+ - labs/lab-03-compute/how-to-guides/test-limits-enforcement.md
+ - labs/lab-03-compute/how-to-guides/custom-healthcheck.md
+ - labs/lab-03-compute/how-to-guides/instance-type-mapping.md
+ - labs/lab-03-compute/reference/compose-resources-syntax.md
+ - labs/lab-03-compute/reference/healthcheck-syntax.md
+ - labs/lab-03-compute/reference/ec2-instance-mapping.md
+ - labs/lab-03-compute/explanation/compute-ec2-parallels.md
+autonomous: true
+requirements:
+ - DOCT-01
+ - DOCT-02
+ - DOCT-03
+ - DOCT-04
+ - DOCT-05
+ - LAB-03
+ - PARA-01
+ - PARA-03
+ - PARA-04
+user_setup: []
+
+must_haves:
+ truths:
+ - "All 4 Diátxis document types created (Tutorial, How-to, Reference, Explanation)"
+ - "Tutorials follow 'little often' principle with incremental steps"
+ - "How-to guides are task-focused and procedure-oriented"
+ - "Reference documents provide complete technical specifications"
+ - "Explanation document clearly maps Docker compute to EC2 concepts"
+ - "Minimum line requirements met for all documents"
+ artifacts:
+ - path: "labs/lab-03-compute/tutorial/01-set-resource-limits.md"
+ provides: "Step-by-step resource limits guide"
+ min_lines: 250
+ - path: "labs/lab-03-compute/tutorial/02-implement-healthchecks.md"
+ provides: "Step-by-step healthcheck guide"
+ min_lines: 250
+ - path: "labs/lab-03-compute/tutorial/03-dependencies-with-health.md"
+ provides: "Step-by-step dependency guide"
+ min_lines: 250
+ - path: "labs/lab-03-compute/how-to-guides/check-resource-usage.md"
+ provides: "Resource monitoring procedure"
+ min_lines: 60
+ - path: "labs/lab-03-compute/how-to-guides/test-limits-enforcement.md"
+ provides: "Limits testing procedure"
+ min_lines: 60
+ - path: "labs/lab-03-compute/how-to-guides/custom-healthcheck.md"
+ provides: "Custom healthcheck procedure"
+ min_lines: 60
+ - path: "labs/lab-03-compute/how-to-guides/instance-type-mapping.md"
+ provides: "Instance type selection guide"
+ min_lines: 60
+ - path: "labs/lab-03-compute/reference/compose-resources-syntax.md"
+ provides: "Complete resources syntax reference"
+ min_lines: 120
+ - path: "labs/lab-03-compute/reference/healthcheck-syntax.md"
+ provides: "Complete healthcheck syntax reference"
+ min_lines: 100
+ - path: "labs/lab-03-compute/reference/ec2-instance-mapping.md"
+ provides: "EC2 instance mapping reference"
+ min_lines: 80
+ - path: "labs/lab-03-compute/explanation/compute-ec2-parallels.md"
+ provides: "Compute-to-EC2 conceptual mapping"
+ min_lines: 280
+ key_links:
+ - from: "tutorial/01-set-resource-limits.md"
+ to: "reference/compose-resources-syntax.md"
+ via: "Tutorial links to syntax reference"
+ pattern: "\\[.*Syntax.*\\].*compose-resources-syntax"
+ - from: "tutorial/02-implement-healthchecks.md"
+ to: "reference/healthcheck-syntax.md"
+ via: "Tutorial links to healthcheck reference"
+ pattern: "\\[.*Reference.*\\].*healthcheck-syntax"
+ - from: "explanation/compute-ec2-parallels.md"
+ to: "reference/ec2-instance-mapping.md"
+ via: "Explanation links to instance mapping"
+ pattern: "\\[.*Instance.*\\].*ec2-instance-mapping"
+---
+
+
+Create comprehensive Diátxis documentation for Lab 03 (Compute & EC2) covering resource limits, healthchecks, and EC2 instance type parallels.
+
+Purpose: Provide students with 4-quadrant documentation following Diátaxis framework: Tutorials (step-by-step learning), How-to Guides (task-focused procedures), Reference (technical specifications), and Explanation (conceptual mapping to EC2).
+
+Output: 11 markdown documents (3 tutorials, 4 how-to guides, 3 reference, 1 explanation) totaling 1800+ lines.
+
+
+
+@/home/luca/.claude/get-shit-done/workflows/execute-plan.md
+@/home/luca/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/REQUIREMENTS.md
+@.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md
+@.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md
+@.planning/phases/03-lab-02-network-vpc/03-02-PLAN.md
+@labs/lab-02-network/explanation/docker-network-vpc-parallels.md
+
+# Diátaxis Framework Overview
+
+From PROJECT.md and Phase 2/3 patterns:
+
+## 1. Tutorials - Learning-Oriented
+- **Purpose:** Guida passo-passo per studenti nuovi
+- **Tone:** Diretto, semplice, incrementale ("little often")
+- **Structure:**
+ - Clear learning objectives
+ - Prerequisites listed
+ - Step-by-step instructions
+ - Verification commands after each step
+ - Troubleshooting tips
+
+## 2. How-to Guides - Task-Oriented
+- **Purpose:** Procedure specifiche e task-focused
+- **Tone:** Pratico, diretto al punto
+- **Structure:**
+ - Single specific task
+ - Prerequisites brief
+ - Step-by-step procedure
+ - Expected results
+ - Common issues
+
+## 3. Reference - Information-Oriented
+- **Purpose:** Specifiche tecniche nude e crude
+- **Tone:** Formale, completo, conciso
+- **Structure:**
+ - Complete parameter reference
+ - All options documented
+ - Examples for each option
+ - No tutorial content
+
+## 4. Explanation - Understanding-Oriented
+- **Purpose:** Parallelismi concettuali locale ↔ cloud
+- **Tone:** Educativo, comparativo
+- **Structure:**
+ - Concept introduction
+ - Side-by-side comparisons
+ - Key differences highlighted
+ - When to use what
+
+# Lab 03 Content Guidelines
+
+## EC2 Instance Type Parallels
+
+**Core Concept:** Docker resource limits simulate EC2 instance types
+
+| Docker | EC2 | Use Case |
+|--------|-----|----------|
+| cpus: '0.5', memory: 512M | t2.nano | Dev/test |
+| cpus: '1', memory: 1G | t2.micro | Small services |
+| cpus: '1', memory: 2G | t2.small | Web servers |
+| cpus: '2', memory: 4G | t2.medium | Application servers |
+| cpus: '2', memory: 8G | m5.large | Production apps |
+| cpus: '4', memory: 16G | m5.xlarge | High-traffic services |
+
+**Key Teaching Points:**
+1. Resource limits = Cost control (pay for what you use)
+2. Different instance types for different workloads
+3. Burstable (t2/t3) vs. consistent performance (m5/c5)
+4. Right-sizing prevents over-provisioning
+
+## Healthcheck Parallels
+
+**Core Concept:** Docker healthchecks simulate ELB health checks
+
+| Docker | AWS |
+|--------|-----|
+| healthcheck.test | ELB health check path |
+| healthcheck.interval | ELB interval (default 30s) |
+| healthcheck.timeout | ELB timeout (default 5s) |
+| healthcheck.retries | ELB unhealthy threshold |
+| healthcheck.start_period | ELB grace period |
+| docker ps --filter health=healthy | ELB target health |
+
+**Key Teaching Points:**
+1. Healthchecks detect failing services
+2. Dependencies wait for healthy status
+3. Prevents cascading failures
+4. Enables zero-downtime deployments
+
+# Documentation Structure
+
+## Tutorial 1: Set Resource Limits
+**Learning Objectives:**
+- Understand EC2 instance types
+- Learn Docker Compose resource syntax
+- Set CPU and memory limits
+- Verify with docker stats
+
+**Outline:**
+1. What are EC2 Instance Types? (5 min)
+2. Docker Resource Limits Syntax (10 min)
+3. Practice: Set limits for t2.micro (15 min)
+4. Practice: Set limits for t2.small (15 min)
+5. Verification with docker stats (5 min)
+
+## Tutorial 2: Implement Healthchecks
+**Learning Objectives:**
+- Understand healthcheck purpose
+- Learn healthcheck syntax
+- Add healthcheck to web service
+- Monitor health status
+
+**Outline:**
+1. What are Healthchecks? (5 min)
+2. ELB Health Check Parallel (5 min)
+3. Healthcheck Parameters (10 min)
+4. Practice: Add HTTP healthcheck (15 min)
+5. Practice: Add database healthcheck (10 min)
+6. Monitor health status (5 min)
+
+## Tutorial 3: Dependencies with Health
+**Learning Objectives:**
+- Understand service dependencies
+- Use depends_on with conditions
+- Implement ordered startup
+- Verify dependency chain
+
+**Outline:**
+1. Service Dependencies (5 min)
+2. depends_on Conditions (10 min)
+3. Practice: Web depends on App (15 min)
+4. Practice: App depends on DB (15 min)
+5. Verify startup order (5 min)
+
+# Implementation Notes
+
+1. **File Locations:**
+```
+labs/lab-03-compute/
+├── tutorial/
+│ ├── 01-set-resource-limits.md
+│ ├── 02-implement-healthchecks.md
+│ └── 03-dependencies-with-health.md
+├── how-to-guides/
+│ ├── check-resource-usage.md
+│ ├── test-limits-enforcement.md
+│ ├── custom-healthcheck.md
+│ └── instance-type-mapping.md
+├── reference/
+│ ├── compose-resources-syntax.md
+│ ├── healthcheck-syntax.md
+│ └── ec2-instance-mapping.md
+├── explanation/
+│ └── compute-ec2-parallels.md
+└── README.md
+```
+
+2. **Code Examples:**
+ - Use realistic examples (nginx, postgres, redis)
+ - Show complete docker-compose.yml snippets
+ - Include verification commands
+ - Add expected output
+
+3. **Parallelism Emphasis:**
+ - Always show AWS equivalent
+ - Explain "why" not just "how"
+ - Highlight key differences
+ - Link to AWS documentation
+
+# Success Criteria
+
+Plan 04-02 is complete when:
+1. All 11 documents created
+2. Minimum line requirements met
+3. Tutorials follow "little often" principle
+4. How-to guides are task-focused
+5. Reference documents are complete specifications
+6. Explanation clearly maps Docker → EC2
+7. Cross-references between documents
+8. Italian language (consistent with other labs)
+
+
+
+1. Create tutorial/ directory
+2. Create tutorial/01-set-resource-limits.md (250+ lines)
+ - EC2 instance types introduction
+ - Docker resource limits syntax
+ - Practice exercises
+ - Verification steps
+3. Create tutorial/02-implement-healthchecks.md (250+ lines)
+ - Healthcheck concept and ELB parallel
+ - Healthcheck parameters
+ - Practice: HTTP healthcheck
+ - Practice: Database healthcheck
+4. Create tutorial/03-dependencies-with-health.md (250+ lines)
+ - Service dependency concepts
+ - depends_on conditions
+ - Practice: Multi-tier dependencies
+ - Startup order verification
+5. Create how-to-guides/ directory
+6. Create how-to-guides/check-resource-usage.md (60+ lines)
+ - docker stats usage
+ - Real-time monitoring
+ - Interpreting output
+7. Create how-to-guides/test-limits-enforcement.md (60+ lines)
+ - CPU limit testing
+ - Memory OOM testing
+ - Verification procedures
+8. Create how-to-guides/custom-healthcheck.md (60+ lines)
+ - Writing custom healthcheck commands
+ - Best practices
+ - Debugging failures
+9. Create how-to-guides/instance-type-mapping.md (60+ lines)
+ - Docker limits → EC2 mapping
+ - Selecting appropriate types
+ - Cost considerations
+10. Create reference/ directory
+11. Create reference/compose-resources-syntax.md (120+ lines)
+ - Complete deploy.resources reference
+ - CPU and memory syntax
+ - Reservations vs limits
+ - All options with examples
+12. Create reference/healthcheck-syntax.md (100+ lines)
+ - All healthcheck parameters
+ - Test command formats
+ - Status values
+ - Examples for each service type
+13. Create reference/ec2-instance-mapping.md (80+ lines)
+ - Complete mapping table
+ - Instance type descriptions
+ - Use case recommendations
+14. Create explanation/ directory
+15. Create explanation/compute-ec2-parallels.md (280+ lines)
+ - Container = EC2 Instance
+ - Resource limits = Instance types
+ - Healthcheck = ELB/Status checks
+ - Docker stats = CloudWatch
+ - Key differences (credits, pricing, etc.)
+ - Command equivalents table
+
diff --git a/.planning/phases/04-lab-03-compute-ec2/04-03-PLAN.md b/.planning/phases/04-lab-03-compute-ec2/04-03-PLAN.md
new file mode 100644
index 0000000..360ce31
--- /dev/null
+++ b/.planning/phases/04-lab-03-compute-ec2/04-03-PLAN.md
@@ -0,0 +1,335 @@
+---
+phase: 04-lab-03-compute-ec2
+plan: 03
+type: execute
+wave: 2
+depends_on:
+ - "04-01"
+ - "04-02"
+files_modified:
+ - labs/lab-03-compute/docker-compose.yml
+ - labs/lab-03-compute/Dockerfile
+ - labs/lab-03-compute/tests/04-verify-infrastructure.sh
+autonomous: true
+requirements:
+ - LAB-03
+ - INF-01
+ - INF-03
+ - PARA-01
+ - PARA-02
+ - TEST-01
+ - TEST-05
+user_setup: []
+
+must_haves:
+ truths:
+ - "docker-compose.yml exists and is valid (docker compose config passes)"
+ - "All services have deploy.resources.limits.cpus set (INF-03)"
+ - "All services have deploy.resources.limits.memory set (INF-03)"
+ - "Services have appropriate healthchecks defined"
+ - "depends_on uses condition: service_healthy where appropriate"
+ - "Infrastructure verification passes all checks"
+ - "Cloud nomenclature follows EC2 instance patterns (PARA-02)"
+ artifacts:
+ - path: "labs/lab-03-compute/docker-compose.yml"
+ provides: "Compute infrastructure with limits and healthchecks"
+ min_lines: 100
+ - path: "labs/lab-03-compute/Dockerfile"
+ provides: "Test container image with stress tools"
+ min_lines: 25
+ - path: "labs/lab-03-compute/tests/04-verify-infrastructure.sh"
+ provides: "Infrastructure verification script"
+ min_lines: 100
+ key_links:
+ - from: "docker-compose.yml"
+ to: "tests/01-resource-limits-test.sh"
+ via: "Tests validate deploy.resources.limits"
+ pattern: "deploy:.*resources:.*limits"
+ - from: "docker-compose.yml"
+ to: "tests/02-healthcheck-test.sh"
+ via: "Tests validate healthcheck sections"
+ pattern: "healthcheck:"
+ - from: "docker-compose.yml"
+ to: "reference/compose-resources-syntax.md"
+ via: "Reference documents all resource options"
+ pattern: "deploy:.*resources"
+ - from: "docker-compose.yml"
+ to: "explanation/compute-ec2-parallels.md"
+ via: "Instance types mapped to EC2"
+ pattern: "# EC2|t2\\.micro|m5\\.large"
+---
+
+
+Implement compute infrastructure for Lab 03 (Compute & EC2) with Docker Compose resource limits and healthchecks. Create docker-compose.yml with services that have mandatory CPU/memory limits (INF-03 compliance) and healthchecks for readiness verification.
+
+Purpose: GREEN phase implementation - make tests from Plan 04-01 pass by implementing compute infrastructure with proper resource limits and healthchecks.
+
+Output: docker-compose.yml with 4+ services, Dockerfile for test container, and infrastructure verification script.
+
+
+
+@/home/luca/.claude/get-shit-done/workflows/execute-plan.md
+@/home/luca/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/REQUIREMENTS.md
+@.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md
+@.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md
+@.planning/phases/03-lab-02-network-vpc/03-03-PLAN.md
+@labs/lab-02-network/docker-compose.yml
+
+# Infrastructure Requirements
+
+## INF-03: Mandatory Resource Limits
+
+**CRITICAL:** Every service MUST have:
+```yaml
+deploy:
+ resources:
+ limits:
+ cpus: 'X' # REQUIRED
+ memory: 'XG' # REQUIRED
+```
+
+**NON-COMPLIANT:**
+```yaml
+# Missing limits - INF-03 VIOLATION
+services:
+ app:
+ image: nginx
+ # No deploy section
+```
+
+## Service Configuration
+
+### Tier 1: Web Server (t2.micro parallel)
+```yaml
+web:
+ image: nginx:alpine
+ container_name: lab03-web
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 1G
+ healthcheck:
+ test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
+ interval: 10s
+ timeout: 5s
+ retries: 3
+ start_period: 5s
+```
+
+### Tier 2: Application Server (t2.small parallel)
+```yaml
+app:
+ image: nginx:alpine
+ container_name: lab03-app
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 2G
+ depends_on:
+ web:
+ condition: service_healthy
+ healthcheck:
+ test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
+ interval: 10s
+ timeout: 5s
+ retries: 3
+```
+
+### Tier 3: Worker (t2.medium parallel)
+```yaml
+worker:
+ image: alpine:3.19
+ container_name: lab03-worker
+ command: ["sh", "-c", "sleep 3600"]
+ deploy:
+ resources:
+ limits:
+ cpus: '2'
+ memory: 4G
+ healthcheck:
+ test: ["CMD-SHELL", "exit 0"]
+ interval: 30s
+ timeout: 5s
+ retries: 3
+```
+
+### Tier 4: Database (t2.medium parallel)
+```yaml
+db:
+ image: postgres:16-alpine
+ container_name: lab03-db
+ environment:
+ POSTGRES_DB: lab03_db
+ POSTGRES_USER: lab03_user
+ POSTGRES_PASSWORD: lab03_password
+ deploy:
+ resources:
+ limits:
+ cpus: '2'
+ memory: 4G
+ volumes:
+ - db-data:/var/lib/postgresql/data
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U lab03_user -d lab03_db"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 10s
+```
+
+### Test Container (for enforcement testing)
+```yaml
+stress-test:
+ image: polinux/stress
+ container_name: lab03-stress
+ command: ["--cpu", "1", "--vm", "1", "--vm-bytes", "256M", "--timeout", "30s"]
+ deploy:
+ resources:
+ limits:
+ cpus: '0.5'
+ memory: 512M
+ healthcheck:
+ test: ["CMD-SHELL", "exit 0"]
+ interval: 5s
+ timeout: 3s
+ retries: 3
+```
+
+# Dockerfile for Test Container
+
+```dockerfile
+# Dockerfile for Lab 03 - Compute & EC2
+# Test container with stress testing tools
+
+FROM alpine:3.19
+
+# Create non-root user (INF-01 compliance)
+RUN addgroup -g 1000 appgroup && \
+ adduser -D -u 1000 -G appgroup appuser
+
+# Install stress testing tools
+RUN apk add --no-cache \
+ stress \
+ curl \
+ && rm -rf /var/cache/apk/*
+
+# Switch to non-root user
+USER appuser
+
+WORKDIR /home/appuser
+
+# Default command - ready for stress testing
+CMD ["sh", "-c", "sleep 3600"]
+```
+
+# Healthcheck Best Practices
+
+## HTTP Service Healthcheck
+```yaml
+test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
+interval: 10s # Check every 10 seconds
+timeout: 5s # Fail after 5 seconds
+retries: 3 # Unhealthy after 3 failures
+start_period: 5s # Grace period on startup
+```
+
+## Database Healthcheck
+```yaml
+test: ["CMD-SHELL", "pg_isready -U postgres || exit 1"]
+interval: 10s
+timeout: 5s
+retries: 5 # More retries for DB (slower startup)
+start_period: 10s # Longer grace period
+```
+
+## Simple Healthcheck
+```yaml
+test: ["CMD-SHELL", "exit 0"]
+interval: 30s # Less frequent for simple checks
+timeout: 3s
+retries: 3
+```
+
+# Infrastructure Verification Script
+
+Based on labs/lab-02-network/tests/04-verify-infrastructure.sh pattern:
+
+## Verification Steps
+
+1. **File Existence:** docker-compose.yml exists
+2. **Syntax Validation:** docker compose config passes
+3. **Resource Limits:** All services have cpus and memory limits
+4. **Healthchecks:** All services have healthcheck sections
+5. **INF-03 Compliance:** 100% of services have limits
+6. **Deploy Services:** docker compose up -d succeeds
+7. **Health Status:** Services become healthy
+8. **Resource Enforcement:** docker stats shows limits
+9. **Dependency Order:** Services start in correct order
+10. **Final Report:** Pass/fail summary
+
+# Cloud Nomenclature (PARA-02)
+
+Service names should reflect EC2 instance parallels:
+- `web` → Web tier (t2.micro)
+- `app` → Application tier (t2.small)
+- `worker` → Background processing (t2.medium)
+- `db` → Database tier (t2.medium)
+
+# Implementation Notes
+
+1. **Version:** Use `version: "3.8"` for compatibility
+2. **Networks:** Can reuse networks from Lab 02 or create new
+3. **Volumes:** Use named volumes for database persistence
+4. **Security:** Follow INF-01 (no root), INF-02 (no 0.0.0.0 bindings)
+5. **Parallelism:** Comments should show EC2 equivalent
+
+# Success Criteria
+
+Plan 04-03 is complete when:
+1. docker-compose.yml created with 4+ services
+2. All services have resource limits (INF-03)
+3. All services have healthchecks
+4. docker compose config validates
+5. Services deploy and become healthy
+6. Infrastructure verification passes all checks
+7. Tests from 04-01 now pass (GREEN phase)
+
+
+
+1. Create labs/lab-03-compute/ directory structure
+2. Create docker-compose.yml (100+ lines)
+ - Service: web (nginx, t2.micro: 1 CPU, 1G RAM)
+ - Service: app (nginx, t2.small: 1 CPU, 2G RAM)
+ - Service: worker (alpine, t2.medium: 2 CPU, 4G RAM)
+ - Service: db (postgres, t2.medium: 2 CPU, 4G RAM)
+ - Service: stress-test (enforcement testing)
+ - All services: deploy.resources.limits
+ - All services: healthcheck sections
+ - Proper depends_on with conditions
+ - Named volumes for database
+3. Create Dockerfile (25+ lines)
+ - Alpine 3.19 base
+ - Non-root user (INF-01)
+ - Install stress tools
+ - Minimal and secure
+4. Create tests/04-verify-infrastructure.sh (100+ lines)
+ - Verify docker-compose.yml exists
+ - Validate syntax
+ - Check INF-03 compliance
+ - Verify healthchecks
+ - Deploy and test services
+ - Check resource enforcement
+ - Final summary report
+5. Test infrastructure:
+ - docker compose config
+ - docker compose up -d
+ - docker stats verification
+ - health status check
+ - docker compose down
+
diff --git a/.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md b/.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md
new file mode 100644
index 0000000..c0d3a8b
--- /dev/null
+++ b/.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md
@@ -0,0 +1,209 @@
+# Phase 4 Research - Lab 03: Compute & EC2
+
+## Domain Research: Docker Resource Limits & Healthchecks
+
+### 1. Docker Compose Resource Limits
+
+**CPU Limits:**
+```yaml
+services:
+ app:
+ deploy:
+ resources:
+ limits:
+ cpus: '0.5' # 50% of 1 CPU core
+ # OR
+ cpus: '2' # 2 full CPU cores
+```
+
+**Memory Limits:**
+```yaml
+services:
+ app:
+ deploy:
+ resources:
+ limits:
+ memory: 512M # 512 MB
+ # OR
+ memory: 2G # 2 GB
+```
+
+**Non-Swap Memory:**
+```yaml
+services:
+ app:
+ deploy:
+ resources:
+ limits:
+ memory: 512M
+ reservations:
+ memory: 256M
+```
+
+### 2. EC2 Instance Types Parallel
+
+| Docker Limits | EC2 Equivalent | Instance Type |
+|---------------|----------------|---------------|
+| cpus: '0.5', memory: 512M | t2.nano (0.5 vCPU, 512MB) | Burstable |
+| cpus: '1', memory: 1G | t2.micro (1 vCPU, 1GB) | Burstable |
+| cpus: '1', memory: 2G | t2.small (1 vCPU, 2GB) | Burstable |
+| cpus: '2', memory: 4G | t2.medium (2 vCPU, 4GB) | Burstable |
+| cpus: '2', memory: 8G | m5.large (2 vCPU, 8GB) | General Purpose |
+| cpus: '4', memory: 16G | m5.xlarge (4 vCPU, 16GB) | General Purpose |
+
+**Key Parallelism:**
+- Docker CPU fractions = AWS vCPUs
+- Docker memory limits = AWS instance memory
+- No swap enforcement = AWS EBS-optimized instances
+- Resource reservations = AWS instance type guarantees
+
+### 3. Healthcheck Implementation
+
+**Docker Compose Healthcheck:**
+```yaml
+services:
+ web:
+ image: nginx:alpine
+ healthcheck:
+ test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80"]
+ interval: 10s # Check every 10 seconds
+ timeout: 5s # Timeout after 5 seconds
+ retries: 3 # Mark unhealthy after 3 failures
+ start_period: 10s # Grace period on startup
+```
+
+**Healthcheck with curl:**
+```yaml
+healthcheck:
+ test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 5s
+```
+
+**Database Healthcheck:**
+```yaml
+db:
+ image: postgres:16-alpine
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U postgres || exit 1"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 10s
+```
+
+**Application Healthcheck:**
+```yaml
+app:
+ image: myapp:latest
+ healthcheck:
+ test: ["CMD-SHELL", "node healthcheck.js || exit 1"]
+ interval: 15s
+ timeout: 3s
+ retries: 3
+ start_period: 30s
+```
+
+### 4. Service Dependencies with Health
+
+**Wait for healthy service:**
+```yaml
+services:
+ app:
+ depends_on:
+ db:
+ condition: service_healthy
+ redis:
+ condition: service_started
+```
+
+**Lifecycle:**
+1. `service_started`: Container started (default)
+2. `service_healthy`: Healthcheck passing (requires healthcheck section)
+
+### 5. Resource Monitoring
+
+**docker stats:**
+```bash
+docker stats --no-stream # Single snapshot
+docker stats lab03-app --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}"
+```
+
+**Inspect limits:**
+```bash
+docker inspect lab03-app --format '{{.HostConfig.Memory}}' # Memory limit in bytes
+docker inspect lab03-app --format '{{.HostConfig.NanoCpus}}' # CPU quota (1e9 = 1 CPU)
+```
+
+## Testing Strategy
+
+### Test Scenarios
+
+1. **Resource Limit Enforcement:**
+ - Deploy container with CPU limit (e.g., 0.5 CPU)
+ - Run CPU-intensive task
+ - Verify with `docker stats` that CPU usage doesn't exceed limit
+
+2. **Memory Limit Enforcement:**
+ - Deploy container with memory limit (e.g., 512M)
+ - Run memory allocation task
+ - Verify container is OOM killed when exceeding limit
+
+3. **Healthcheck Validation:**
+ - Deploy service with healthcheck
+ - Verify status transitions: starting → healthy
+ - Verify `depends_on: condition: service_healthy` waits
+
+4. **Resource Verification:**
+ - Parse docker-compose.yml for `deploy.resources.limits`
+ - Verify all services have mandatory limits
+ - Report missing limits
+
+## Security Requirements
+
+**INF-03: Mandatory Resource Limits**
+- Every container MUST have `cpus` limit specified
+- Every container MUST have `memory` limit specified
+- No container can run with unlimited resources (security risk)
+
+**Safety First:**
+- Resource limits prevent DoS from runaway processes
+- Memory limits prevent host OOM
+- CPU limits ensure fair resource sharing
+
+## Cloud Parallels
+
+### Docker → AWS EC2
+
+| Docker | AWS EC2 |
+|--------|---------|
+| `cpus: '0.5'` | 0.5 vCPU (t2.nano) |
+| `memory: 512M` | 512 MB RAM |
+| `healthcheck` | EC2 Status Checks + ELB Health |
+| `docker stats` | CloudWatch Metrics |
+| `OOM kill` | Instance termination (out of credit) |
+| `depends_on: healthy` | Auto Scaling Group health checks |
+
+### Instance Type Selection
+
+**Burstable Instances (t2/t3):**
+- Credit-based CPU
+- Good for dev/test
+- Docker: Small limits with occasional bursts
+
+**General Purpose (m5):**
+- Balanced compute/memory
+- Docker: Medium limits (2-4 vCPU, 8-16 GB)
+
+**Compute Optimized (c5):**
+- High CPU ratio
+- Docker: High CPU limits (4+ vCPU, lower memory)
+
+## Sources
+
+- [Docker Compose Resources](https://docs.docker.com/compose/compose-file/compose-file-v3/#resources)
+- [Docker Healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck)
+- [AWS EC2 Instance Types](https://aws.amazon.com/ec2/instance-types/)
+- [Docker Stats](https://docs.docker.com/engine/reference/commandline/stats/)
diff --git a/.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md b/.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md
new file mode 100644
index 0000000..704b171
--- /dev/null
+++ b/.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md
@@ -0,0 +1,247 @@
+# Phase 4 Validation - Lab 03: Compute & EC2
+
+## Validation Strategy
+
+### Goal-Backward Verification
+
+We verify that Phase 4 achieves its goals by checking the success criteria from the Phase definition.
+
+## Success Criteria (from ROADMAP)
+
+1. **Studente può deploy container con limiti CPU/memoria obbligatori** (simulazione instance types)
+2. **Studente può implementare healthchecks per verificare che servizi siano "healthy"**
+3. **Tutti i container hanno `cpus` e `mem_limit` configurati** (enforcement risorse cloud)
+4. **Studente comprende il parallelismo tra container con limiti e EC2 instances**
+5. **Lab include test che verificano resource limits con `docker stats` e healthcheck readiness**
+
+## Validation Checklist
+
+### 04-01: Test Infrastructure (RED Phase)
+
+**Acceptance Criteria:**
+- [ ] Test script `01-resource-limits-test.sh` validates:
+ - Parse docker-compose.yml for `deploy.resources.limits.cpus`
+ - Parse docker-compose.yml for `deploy.resources.limits.memory`
+ - Report services without mandatory limits
+ - Test should FAIL initially (no limits configured)
+- [ ] Test script `02-healthcheck-test.sh` validates:
+ - Services have `healthcheck` section defined
+ - Healthcheck command is valid
+ - `interval`, `timeout`, `retries` configured
+ - Test should FAIL initially (no healthchecks configured)
+- [ ] Test script `03-resource-enforcement-test.sh` validates:
+ - Deploy test container with limits
+ - Run CPU-intensive task
+ - Verify `docker stats` shows enforcement
+ - Run memory allocation task
+ - Verify OOM kill on exceed
+- [ ] Final verification `99-final-verification.sh` validates:
+ - All services have resource limits
+ - All services have healthchecks
+ - Healthchecks pass
+ - Resource limits enforced
+ - INF-03 compliance verified
+
+**Verification Command:**
+```bash
+cd labs/lab-03-compute
+bash tests/run-all-tests.sh
+```
+
+**Expected Result:** All tests FAIL initially (RED phase), then PASS after implementation (GREEN phase)
+
+---
+
+### 04-02: Diátxis Documentation
+
+**Acceptance Criteria:**
+
+**Tutorials (3):**
+- [ ] `tutorial/01-set-resource-limits.md` (min 250 lines)
+ - Explain EC2 instance types concept
+ - Show Docker CPU/memory limits syntax
+ - Practice: Set limits for different instance types
+ - Verify with `docker stats`
+- [ ] `tutorial/02-implement-healthchecks.md` (min 250 lines)
+ - Explain healthcheck purpose (ELB parallel)
+ - Show healthcheck syntax and parameters
+ - Practice: Add healthcheck to web service
+ - Verify with `docker ps` and health status
+- [ ] `tutorial/03-dependencies-with-health.md` (min 250 lines)
+ - Explain service dependencies
+ - Show `depends_on: condition: service_healthy`
+ - Practice: Multi-tier with health-based startup
+ - Verify startup order
+
+**How-to Guides (4):**
+- [ ] `how-to-guides/check-resource-usage.md` (min 60 lines)
+ - How to use `docker stats`
+ - How to inspect limits
+ - How to monitor in real-time
+- [ ] `how-to-guides/test-limits-enforcement.md` (min 60 lines)
+ - How to trigger CPU limit
+ - How to trigger memory OOM
+ - How to verify enforcement
+- [ ] `how-to-guides/custom-healthcheck.md` (min 60 lines)
+ - How to write custom healthcheck
+ - Healthcheck best practices
+ - Debugging healthcheck failures
+- [ ] `how-to-guides/instance-type-mapping.md` (min 60 lines)
+ - Docker limits → EC2 instance mapping
+ - Selecting appropriate instance type
+ - Cost optimization parallels
+
+**Reference (3):**
+- [ ] `reference/compose-resources-syntax.md` (min 120 lines)
+ - Complete `deploy.resources` reference
+ - CPU and memory limit syntax
+ - Reservations vs limits
+- [ ] `reference/healthcheck-syntax.md` (min 100 lines)
+ - Healthcheck parameters
+ - Test command formats
+ - Status values and transitions
+- [ ] `reference/ec2-instance-mapping.md` (min 80 lines)
+ - Docker limits → EC2 instance table
+ - Parallelism documentation
+ - Command equivalents
+
+**Explanation (1):**
+- [ ] `explanation/compute-ec2-parallels.md` (min 280 lines)
+ - Container = EC2 Instance
+ - Resource limits = Instance types
+ - Healthcheck = ELB/Status checks
+ - Docker stats = CloudWatch
+ - Differences (credits, pricing, etc.)
+
+**Verification:**
+```bash
+find labs/lab-03-compute -name "*.md" | wc -l # Expect: 11
+wc -l labs/lab-03-compute/tutorial/*.md # Expect: 750+
+wc -l labs/lab-03-compute/how-to-guides/*.md # Expect: 240+
+wc -l labs/lab-03-compute/reference/*.md # Expect: 300+
+wc -l labs/lab-03-compute/explanation/*.md # Expect: 280+
+```
+
+---
+
+### 04-03: Infrastructure Implementation (GREEN Phase)
+
+**Acceptance Criteria:**
+- [ ] `docker-compose.yml` exists and is valid
+- [ ] All services have `deploy.resources.limits.cpus` set
+- [ ] All services have `deploy.resources.limits.memory` set
+- [ ] Services have appropriate healthchecks defined
+- [ ] Healthcheck parameters are reasonable (interval, timeout, retries)
+- [ ] `depends_on` uses `condition: service_healthy` where appropriate
+- [ ] INF-03 compliance: NO service without resource limits
+- [ ] Cloud nomenclature: Service names follow EC2 instance patterns
+- [ ] Infrastructure verification passes all checks
+
+**INF-03 Compliance Check:**
+```bash
+grep -c "deploy:" labs/lab-03-compute/docker-compose.yml # Should equal service count
+grep -c "cpus:" labs/lab-03-compute/docker-compose.yml # Should equal service count
+grep -c "memory:" labs/lab-03-compute/docker-compose.yml # Should equal service count
+```
+
+**Services Configuration:**
+
+| Service | Instance Type Parallel | CPUs | Memory | Healthcheck |
+|---------|----------------------|------|--------|-------------|
+| web | t2.micro | 1 | 1G | HTTP endpoint |
+| app | t2.small | 1 | 2G | HTTP endpoint |
+| worker | t2.medium | 2 | 4G | Custom command |
+| db | t2.medium | 2 | 4G | pg_isready |
+
+**Verification Command:**
+```bash
+cd labs/lab-03-compute
+bash tests/99-final-verification.sh
+```
+
+**Expected Result:** All checks PASS
+
+---
+
+## Automated Validation Scripts
+
+### Script 1: INF-03 Compliance
+```bash
+#!/bin/bash
+# Verify all services have resource limits
+
+SERVICES=$(docker compose config --services)
+for service in $SERVICES; do
+ has_cpu=$(docker compose config | grep -A 10 "$service:" | grep -c "cpus:")
+ has_mem=$(docker compose config | grep -A 10 "$service:" | grep -c "memory:")
+ if [[ $has_cpu -eq 0 || $has_mem -eq 0 ]]; then
+ echo "FAIL: $service missing resource limits"
+ exit 1
+ fi
+done
+echo "PASS: All services have resource limits"
+```
+
+### Script 2: Healthcheck Verification
+```bash
+#!/bin/bash
+# Verify all services have healthchecks
+
+SERVICES=$(docker compose config --services)
+for service in $SERVICES; do
+ has_hc=$(docker compose config | grep -A 20 "$service:" | grep -c "healthcheck:")
+ if [[ $has_hc -eq 0 ]]; then
+ echo "FAIL: $service missing healthcheck"
+ exit 1
+ fi
+done
+echo "PASS: All services have healthchecks"
+```
+
+### Script 3: Resource Enforcement Test
+```bash
+#!/bin/bash
+# Deploy container with limits and verify enforcement
+
+docker compose up -d test-stress
+sleep 2
+
+# Get limits
+CPU_LIMIT=$(docker inspect test-stress --format='{{.HostConfig.NanoCpus}}')
+MEM_LIMIT=$(docker inspect test-stress --format='{{.HostConfig.Memory}}')
+
+# Start stress test
+docker exec test-stress stress --cpu 1 &
+sleep 5
+
+# Verify CPU doesn't exceed 50%
+CPU_USAGE=$(docker stats test-stress --no-stream --format "{{.CPUPerc}}")
+if [[ $CPU_USAGE > 50 ]]; then
+ echo "FAIL: CPU limit not enforced"
+ exit 1
+fi
+
+echo "PASS: Resource limits enforced"
+```
+
+## Final Validation Checklist
+
+- [ ] All 6 test scripts created (RED phase)
+- [ ] All 11 documentation files created (Diátxis)
+- [ ] docker-compose.yml implemented (GREEN phase)
+- [ ] All tests pass after implementation
+- [ ] INF-03 compliance verified
+- [ ] Student can follow tutorial end-to-end
+- [ ] Parallelism to EC2 documented
+- [ ] Resource limits enforced (verified with docker stats)
+- [ ] Healthchecks functional
+- [ ] Service dependencies work correctly
+
+## Sign-off
+
+Phase 4 is complete when:
+1. Student can deploy container with resource limits
+2. Student can add healthchecks to services
+3. All tests pass (99-final-verification.sh)
+4. Documentation follows Diátxis framework
+5. INF-03 compliance is mandatory and enforced
diff --git a/labs/lab-03-compute/Dockerfile b/labs/lab-03-compute/Dockerfile
new file mode 100644
index 0000000..ab2a406
--- /dev/null
+++ b/labs/lab-03-compute/Dockerfile
@@ -0,0 +1,25 @@
+# Dockerfile per Lab 03 - Compute & EC2
+# Container di test con strumenti di stress per verifica enforcement
+
+FROM alpine:3.19
+
+# Creare utente non-root per sicurezza (INF-01 compliance)
+RUN addgroup -g 1000 appgroup && \
+ adduser -D -u 1000 -G appgroup appuser
+
+# Installare strumenti di stress testing e monitoraggio
+RUN apk add --no-cache \
+ stress \
+ curl \
+ wget \
+ procps \
+ && rm -rf /var/cache/apk/*
+
+# Passare all'utente non-root
+USER appuser
+
+# Set working directory
+WORKDIR /home/appuser
+
+# Comando di default - container in attesa per testing
+CMD ["sh", "-c", "sleep 3600"]
diff --git a/labs/lab-03-compute/docker-compose.yml b/labs/lab-03-compute/docker-compose.yml
new file mode 100644
index 0000000..500d24b
--- /dev/null
+++ b/labs/lab-03-compute/docker-compose.yml
@@ -0,0 +1,140 @@
+# Lab 03: Compute & EC2 - Docker Compose Configuration
+# Simula diverse EC2 instance types usando resource limits Docker
+
+version: "3.8"
+
+services:
+ # Web Server - simula t2.micro (1 vCPU, 1 GB RAM)
+ web:
+ image: nginx:alpine
+ container_name: lab03-web
+ hostname: web
+
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 1G
+
+ healthcheck:
+ test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
+ interval: 10s
+ timeout: 5s
+ retries: 3
+ start_period: 5s
+
+ ports:
+ - "127.0.0.1:8080:80"
+
+ depends_on:
+ app:
+ condition: service_healthy
+
+ restart: unless-stopped
+
+ # Application Server - simula t2.small (1 vCPU, 2 GB RAM)
+ app:
+ image: nginx:alpine
+ container_name: lab03-app
+ hostname: app
+
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 2G
+
+ healthcheck:
+ test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
+ interval: 10s
+ timeout: 5s
+ retries: 3
+ start_period: 5s
+
+ ports:
+ - "127.0.0.1:8081:80"
+
+ depends_on:
+ db:
+ condition: service_healthy
+
+ restart: unless-stopped
+
+ # Worker - simula t2.medium (2 vCPU, 4 GB RAM)
+ worker:
+ image: alpine:3.19
+ container_name: lab03-worker
+ hostname: worker
+
+ command: ["sh", "-c", "sleep 3600"]
+
+ deploy:
+ resources:
+ limits:
+ cpus: '2'
+ memory: 4G
+
+ healthcheck:
+ test: ["CMD-SHELL", "exit 0"]
+ interval: 30s
+ timeout: 5s
+ retries: 3
+
+ restart: unless-stopped
+
+ # Database - simula t2.medium (2 vCPU, 4 GB RAM)
+ db:
+ image: postgres:16-alpine
+ container_name: lab03-db
+ hostname: db
+
+ environment:
+ POSTGRES_DB: lab03_db
+ POSTGRES_USER: lab03_user
+ POSTGRES_PASSWORD: lab03_password
+ POSTGRES_INITDB_ARGS: "-E UTF8"
+
+ deploy:
+ resources:
+ limits:
+ cpus: '2'
+ memory: 4G
+
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U lab03_user -d lab03_db || exit 1"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 10s
+
+ volumes:
+ - db-data:/var/lib/postgresql/data
+
+ restart: unless-stopped
+
+ # Stress Test Container - per verifica enforcement
+ stress-test:
+ image: alpine:3.19
+ container_name: lab03-stress
+ hostname: stress-test
+
+ command: ["sh", "-c", "sleep 3600"]
+
+ deploy:
+ resources:
+ limits:
+ cpus: '0.5'
+ memory: 512M
+
+ healthcheck:
+ test: ["CMD-SHELL", "exit 0"]
+ interval: 5s
+ timeout: 3s
+ retries: 3
+
+ restart: unless-stopped
+
+# Persistent Volumes
+volumes:
+ db-data:
+ driver: local
diff --git a/labs/lab-03-compute/explanation/compute-ec2-parallels.md b/labs/lab-03-compute/explanation/compute-ec2-parallels.md
new file mode 100644
index 0000000..d5276e5
--- /dev/null
+++ b/labs/lab-03-compute/explanation/compute-ec2-parallels.md
@@ -0,0 +1,484 @@
+# Explanation: Parallelismi tra Docker Compute e EC2
+
+In questo documento esploreremo come le risorse dei container Docker simulano le istanze EC2 di AWS. Comprendere questi parallelismi ti permetterà di applicare le conoscenze acquisite localmente agli ambienti cloud reali.
+
+---
+
+## Cos'è un'EC2 Instance?
+
+**EC2 (Elastic Compute Cloud)** Instance è una macchina virtuale nel cloud che:
+- **Fornisce risorse computazionali**: CPU, memoria, storage, rete
+- **Definisce il tipo e la dimensione**: Instance types con diverse combinazioni
+- **Scansiona i costi**: Paghi per le risorse che usi (o riservate)
+
+Le istanze EC2 sono il cuore della compute infrastructure in AWS, usate per:
+- Web server e application server
+- Database (RDS, Aurora)
+- Batch processing
+- Container hosting (ECS, EKS)
+
+---
+
+## Il Parallelismo Fondamentale
+
+### Container Docker = EC2 Instance
+
+| Locale | Cloud AWS |
+|--------|-----------|
+| `docker run` | `aws ec2 run-instances` |
+| Container con limiti | EC2 Instance type |
+| `cpus: '1'` | 1 vCPU |
+| `memory: 2G` | 2 GB RAM |
+| `docker stats` | CloudWatch Metrics |
+| Healthcheck | ELB Health Check |
+
+### Resource Limits = Instance Type
+
+**Locale (Docker Compose):**
+```yaml
+deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 2G
+```
+
+**Cloud (AWS CLI):**
+```bash
+aws ec2 run-instances \
+ --image-id ami-12345 \
+ --instance-type t2.small \
+ # t2.small = 1 vCPU, 2 GB RAM
+```
+
+**Stesso risultato:** 1 vCPU, 2 GB RAM di risorse garantite.
+
+---
+
+## EC2 Instance Types - Approfondimento
+
+### Famiglie di Instance Types
+
+AWS offre diverse famiglie di istanze per diversi workload:
+
+| Famiglia | Prefix | Caratteristiche | Docker Parallel |
+|----------|--------|-----------------|-----------------|
+| **Burstable** | t2, t3 | Credit-based CPU, low cost | Piccoli limiti con burst |
+| **General Purpose** | m5, m6 | Equilibrato CPU/memoria | Limiti bilanciati |
+| **Compute Optimized** | c5, c6 | Alto ratio CPU | CPU alto, memoria bassa |
+| **Memory Optimized** | r5, r6 | Alto ratio memoria | CPU basso, memoria alta |
+| **Storage Optimized** | i3, i4 | NVMe SSD locale | Volumes veloci |
+
+### Instance Types Comuni Analizzati
+
+#### T2 Nano - Microservices
+**Spec:** 0.5 vCPU, 512 MB RAM
+**Costo:** ~$0.006/ora
+**Use Case:** Microservizi minimi, background tasks
+
+**Docker Parallel:**
+```yaml
+deploy:
+ resources:
+ limits:
+ cpus: '0.5'
+ memory: 512M
+```
+
+**Quando usarlo:**
+- Servizi che usano poca CPU
+- Task asincroni leggeri
+- Sviluppo e test economici
+
+#### T2 Micro - Dev/Test
+**Spec:** 1 vCPU, 1 GB RAM
+**Costo:** ~$0.012/ora
+**Use Case:** Development, test, small websites
+
+**Docker Parallel:**
+```yaml
+deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 1G
+```
+
+**Quando usarlo:**
+- Ambienti di sviluppo
+- Test automation
+- Microservices a basso traffico
+
+#### T2 Small - Web Servers
+**Spec:** 1 vCPU, 2 GB RAM
+**Costo:** ~$0.024/ora
+**Use Case:** Web server, API endpoints
+
+**Docker Parallel:**
+```yaml
+deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 2G
+```
+
+**Quando usarlo:**
+- Web server (Nginx, Apache)
+- API REST
+- Container reverse proxy
+
+#### T2 Medium - Application Server
+**Spec:** 2 vCPU, 4 GB RAM
+**Costo:** ~$0.048/ora
+**Use Case:** Application server, cache
+
+**Docker Parallel:**
+```yaml
+deploy:
+ resources:
+ limits:
+ cpus: '2'
+ memory: 4G
+```
+
+**Quando usarlo:**
+- Application server (Node.js, Python)
+- Cache server (Redis)
+- Database di sviluppo
+
+#### M5 Large - Production
+**Spec:** 2 vCPU, 8 GB RAM
+**Costo:** ~$0.096/ora
+**Use Case:** Production applications
+
+**Docker Parallel:**
+```yaml
+deploy:
+ resources:
+ limits:
+ cpus: '2'
+ memory: 8G
+```
+
+**Quando usarlo:**
+- Production web applications
+- Services con cache in-memory
+- Databases di produzione (non critici)
+
+---
+
+## Healthcheck Parallelism
+
+### Docker Healthcheck = ELB Health Check
+
+| Locale | Cloud AWS |
+|--------|-----------|
+| healthcheck.test | Health check path/protocol |
+| healthcheck.interval | Health check interval (30s) |
+| healthcheck.timeout | Health check timeout (5s) |
+| healthcheck.retries | Unhealthy threshold (2) |
+| healthcheck.start_period | Grace period (none in ELB) |
+
+### Esempio Pratico
+
+**Locale (Docker Compose):**
+```yaml
+healthcheck:
+ test: ["CMD", "wget", "--spider", "-q", "http://localhost/health"]
+ interval: 30s
+ timeout: 5s
+ retries: 2
+```
+
+**Cloud (Target Group ELB):**
+```json
+{
+ "TargetGroup": {
+ "HealthCheckProtocol": "HTTP",
+ "HealthCheckPath": "/health",
+ "HealthCheckIntervalSeconds": 30,
+ "HealthCheckTimeoutSeconds": 5,
+ "UnhealthyThresholdCount": 2,
+ "HealthyThresholdCount": 2
+ }
+}
+```
+
+**Stesso comportamento:**
+- Check HTTP ogni 30 secondi
+- Timeout dopo 5 secondi
+- Unhealthy dopo 2 fallimenti consecutivi
+
+---
+
+## Resource Monitoring Parallelism
+
+### Docker Stats = CloudWatch Metrics
+
+| Locale | Cloud AWS |
+|--------|-----------|
+| docker stats | CloudWatch Metrics |
+| CPU % | CPUUtilization |
+| Mem usage | MemoryUtilization |
+| Network I/O | NetworkIn/Out |
+| Block I/O | DiskReadBytes/WriteBytes |
+
+### Esempio di Monitoring
+
+**Locale:**
+```bash
+docker stats lab03-web --no-stream
+# lab03-web: 0.01% CPU, 2.5MiB / 1GiB memory (0.24%)
+```
+
+**Cloud (CloudWatch):**
+```bash
+aws cloudwatch get-metric-statistics \
+ --namespace AWS/EC2 \
+ --metric-name CPUUtilization \
+ --dimensions Name=InstanceId,Value=i-12345 \
+ --start-time 2024-03-25T00:00:00Z \
+ --end-time 2024-03-25T00:05:00Z \
+ --period 60 \
+ --statistics Average
+```
+
+---
+
+## Dipendenze Parallelism
+
+### Docker depends_on = ECS DependsOn
+
+| Locale | Cloud AWS |
+|--------|-----------|
+| depends_on: service_healthy | dependsOn: condition=HEALTHY |
+| depends_on: service_started | dependsOn: condition=START |
+
+### Esempio Multi-Tier
+
+**Locale (Docker Compose):**
+```yaml
+services:
+ web:
+ depends_on:
+ app:
+ condition: service_healthy
+
+ app:
+ depends_on:
+ db:
+ condition: service_healthy
+
+ db:
+ # No dependencies
+```
+
+**Cloud (ECS Task Definition):**
+```json
+{
+ "containerDefinitions": [
+ {
+ "name": "web",
+ "dependsOn": [
+ {"containerName": "app", "condition": "HEALTHY"}
+ ]
+ },
+ {
+ "name": "app",
+ "dependsOn": [
+ {"containerName": "db", "condition": "HEALTHY"}
+ ]
+ },
+ {
+ "name": "db",
+ "dependsOn": []
+ }
+ ]
+}
+```
+
+---
+
+## Costi e Billing Parallelism
+
+### Docker = Local Resources (Free)
+
+In locale, le risorse Docker sono "gratuite":
+- Paghi per l'hardware host (una tantum)
+- Nessun costo orario per container
+- Limiti servono per isolamento, non billing
+
+### EC2 = Pay-Per-Use
+
+In cloud, paghi per:
+- Ore di compute (o secondi con Fargate)
+- Tipi di istanza (più grandi = più costosi)
+- Riserve e spot instances possono ridurre costi
+
+**Mapping costi:**
+
+| Docker | EC2 | Costo/ora |
+|--------|-----|-----------|
+| 0.5 CPU, 512M | t2.nano | ~$0.006 |
+| 1 CPU, 1G | t2.micro | ~$0.012 |
+| 1 CPU, 2G | t2.small | ~$0.024 |
+| 2 CPU, 4G | t2.medium | ~$0.048 |
+| 2 CPU, 8G | m5.large | ~$0.096 |
+
+---
+
+## Scaling Parallelism
+
+### Docker Compose Scale = EC2 Auto Scaling
+
+**Locale (Horizontal Scaling):**
+```yaml
+web:
+ deploy:
+ replicas: 4
+ resources:
+ limits:
+ cpus: '1'
+ memory: 2G
+```
+Risultato: 4 container, ognuno con 1 CPU e 2 GB
+
+**Cloud (Auto Scaling Group):**
+```bash
+aws autoscaling create-auto-scaling-group \
+ --auto-scaling-group-name my-asg \
+ --launch-template LaunchTemplateId=lt-12345 \
+ --min-size 2 \
+ --max-size 4 \
+ --desired-capacity 4
+```
+Risultato: 4 EC2 instances (t2.small)
+
+**Parallelismo:**
+- Docker: `replicas` = numero di container
+- EC2: `desired-capacity` = numero di istanze
+- Entrambi: distribuiscono carico su più unità
+
+---
+
+## Differenze Chiave
+
+### 1. Crediti CPU (Burstable Instances)
+
+**T2/T3 instances** usano CPU credits:
+- Ogni istanza accumula crediti quando idle
+- Spende crediti quando sotto carico
+- Senza crediti = performance degradata
+
+**Docker NON ha credits:**
+- CPU limit è hard cap
+- Mai degrada (sempre disponibile fino al limite)
+- No burst oltre il limite
+
+### 2. Pricing Models
+
+**AWS offre:**
+- **On-Demand:** Paghi per ora usata
+- **Reserved:** Sconto per 1-3 anni di impegno
+- **Spot:** Asta per capacità unused (fino a 90% sconto)
+- **Dedicated:** Host fisico dedicato
+
+**Docker:**
+- Nessun pricing model
+- Tutto flat sul costo host
+
+### 3. Availability Zones
+
+**AWS:**
+- Istanze distribuite su multiple AZ
+- Ogni AZ = data center separato
+- High availability geografica
+
+**Docker:**
+- Single host (senza Swarm/Kubernetes)
+- Nessuna AZ separation
+- Host failure = tutti i container down
+
+---
+
+## Best Practices Transfer
+
+### Da Locale a Cloud
+
+| Best Practice Locale | Equivalente Cloud |
+|---------------------|-------------------|
+| Set resource limits | Choose right instance type |
+| Use healthchecks | Configure ELB health checks |
+| depends_on healthy | Use ECS dependsOn |
+| Monitor with docker stats | Use CloudWatch alarms |
+| Scale with replicas | Use Auto Scaling Groups |
+
+### Evoluzione Architetturale
+
+**Locale (Docker Compose):**
+```yaml
+services:
+ web:
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 2G
+```
+
+**Cloud (ECS/Fargate):**
+```json
+{
+ "containerDefinitions": [{
+ "name": "web",
+ "cpu": 1024,
+ "memory": 2048,
+ "memoryReservation": 2048
+ }]
+}
+```
+
+**Nota:** In ECS Fargate, CPU e memoria sono configurati come:
+- `cpu`: 256, 512, 1024, 2048, 4096 (unità: 1 vCPU = 1024)
+- `memory`: 512, 1024, 2048, ... (in MB)
+
+---
+
+## Command Equivalents Table
+
+| Operazione | Locale (Docker) | Cloud (AWS) |
+|------------|------------------|-------------|
+| **Deploy compute** | docker compose up -d | aws ec2 run-instances |
+| **Check resources** | docker inspect --format '{{.HostConfig}}' | aws ec2 describe-instance-types |
+| **Monitor usage** | docker stats | aws cloudwatch get-metric-statistics |
+| **Set limits** | deploy.resources.limits | --instance-type parameter |
+| **Check health** | docker inspect --format '{{.State.Health.Status}}' | aws elb describe-target-health |
+| **Scale out** | docker compose up -d --scale web=4 | aws autoscaling set-desired-capacity |
+| **Stop compute** | docker compose stop | aws ec2 stop-instances |
+| **Terminate** | docker compose down | aws ec2 terminate-instances |
+
+---
+
+## Conclusione
+
+Le risorse dei container Docker seguono gli stessi principi fondamentali delle EC2 instances: definizione di CPU e memoria, monitoraggio dell'utilizzo, e health checks per verificare lo stato.
+
+Quando lavorerai con EC2 cloud, ricorda:
+
+- **Docker Container** = **EC2 Instance** (unità di compute)
+- **Resource Limits** = **Instance Type** (dimensione e potenza)
+- **Healthcheck** = **ELB Health Check** (verifica stato)
+- **docker stats** = **CloudWatch Metrics** (monitoraggio)
+- **depends_on** = **ECS DependsOn** (ordinamento avvio)
+
+Comprendendo questi parallelismi, sarai in grado di progettare architetture cloud scalabili usando le competenze acquisite localmente.
+
+---
+
+## Approfondimenti
+
+- [AWS EC2 Instance Types Documentation](https://docs.aws.amazon.com/ec2/latest/instancetypes/)
+- [ECS Task Definitions](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html)
+- [ELB Health Checks](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/target-group-health-checks.html)
+- [CloudWatch Metrics](https://docs.aws.amazon.com/AmazonCloudWatch/latest/UserGuide/cloudwatch_concepts.html)
diff --git a/labs/lab-03-compute/how-to-guides/check-resource-usage.md b/labs/lab-03-compute/how-to-guides/check-resource-usage.md
new file mode 100644
index 0000000..7f81950
--- /dev/null
+++ b/labs/lab-03-compute/how-to-guides/check-resource-usage.md
@@ -0,0 +1,94 @@
+# How-to: Verificare l'Utilizzo delle Risorse
+
+Come monitorare l'utilizzo CPU e memoria dei container Docker.
+
+## Utilizzo Base
+
+### Snapshot Singolo
+
+```bash
+docker stats --no-stream
+```
+
+Output:
+```
+CONTAINER NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
+12345 lab03-web 0.01% 2.5MiB / 1GiB 0.24% 1.2kB / 0B 0B / 0B 2
+```
+
+### Monitoraggio in Tempo Reale
+
+```bash
+docker stats
+```
+
+Premi `Ctrl+C` per uscire.
+
+### Container Specifico
+
+```bash
+docker stats lab03-web
+```
+
+## Formattazione Avanzata
+
+### Solo Container e CPU/Memoria
+
+```bash
+docker stats --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}"
+```
+
+### Output senza header
+
+```bash
+docker stats --no-stream --format "{{.Container}}: {{.CPUPerc}}, {{.MemUsage}}"
+```
+
+### Output CSV
+
+```bash
+docker stats --no-stream --format "{{.Container}},{{.CPUPerc}},{{.MemUsage}}"
+```
+
+## Interpretare l'Output
+
+### CPU Percentage
+- `0.01%` - Container idle
+- `50%` - Container usa mezza CPU
+- `100%` - Container usa 1 CPU completa
+- `>100%` - Container usa più di 1 CPU (multi-core)
+
+### Memory Usage
+- `2.5MiB / 1GiB` - Usati 2.5 MB su 1 GB di limite
+- `512MiB / 512MiB` - Al limite (potrebbe causare OOM)
+- `980MiB / 1GiB` - Vicino al limite (watch!)
+
+### Memory Percentage
+- `<50%` - Sotto l'half del limite (OK)
+- `50-80%` - Nella norma (monitorare)
+- `>80%` - Vicino al limite (attenzione)
+- `>95%` - A rischio di OOM kill
+
+## Troubleshooting
+
+### Container usa 0% CPU
+Container potrebbe essere idle o bloccato. Verifica:
+```bash
+docker exec lab03-web ps aux
+```
+
+### Memory usage alto
+Identifica il processo che usa più memoria:
+```bash
+docker exec lab03-web ps aux --sort=-%mem | head -5
+```
+
+### Container OOM killed
+Cerca "OOM" nei log:
+```bash
+docker inspect lab03-web --format '{{.State.OOMKilled}}'
+```
+
+## Vedi Anche
+- How-to: Testare Limits Enforcement
+- Reference: Compose Resources Syntax
diff --git a/labs/lab-03-compute/how-to-guides/custom-healthcheck.md b/labs/lab-03-compute/how-to-guides/custom-healthcheck.md
new file mode 100644
index 0000000..e6eeaaa
--- /dev/null
+++ b/labs/lab-03-compute/how-to-guides/custom-healthcheck.md
@@ -0,0 +1,120 @@
+# How-to: Scrivere Healthchecks Personalizzati
+
+Come creare healthchecks custom per diversi tipi di servizi.
+
+## Pattern Comuni
+
+### HTTP Healthcheck
+
+```yaml
+healthcheck:
+ test: ["CMD-SHELL", "curl -f http://localhost/health || exit 1"]
+ interval: 15s
+ timeout: 3s
+ retries: 3
+```
+
+### TCP Port Check
+
+```yaml
+healthcheck:
+ test: ["CMD-SHELL", "nc -z localhost 8080 || exit 1"]
+ interval: 10s
+ timeout: 2s
+ retries: 3
+```
+
+### File Existence Check
+
+```yaml
+healthcheck:
+ test: ["CMD-SHELL", "test -f /var/run/app/ready || exit 1"]
+ interval: 5s
+ timeout: 1s
+ retries: 5
+```
+
+### Database Connection
+
+```yaml
+healthcheck:
+ test: ["CMD-SHELL", "mysqladmin ping -h localhost || exit 1"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+```
+
+### Redis Check
+
+```yaml
+healthcheck:
+ test: ["CMD", "redis-cli", "ping"]
+ interval: 10s
+ timeout: 3s
+ retries: 3
+```
+
+### Python Script Check
+
+```yaml
+healthcheck:
+ test: ["CMD-SHELL", "python /app/healthcheck.py || exit 1"]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+```
+
+## Best Practices
+
+### 1. Check Leggeri
+```yaml
+# ✓ GOOD - pagina leggera
+test: ["CMD", "wget", "--spider", "-q", "http://localhost/health"]
+
+# ✗ AVOID - pagina pesante
+test: ["CMD", "wget", "--spider", "-q", "http://localhost/big-data"]
+```
+
+### 2. Timeout Appropriati
+```yaml
+# ✓ GOOD - timeout proporzionato all'interval
+interval: 10s
+timeout: 3s
+
+# ✗ AVOID - timeout troppo lungo
+interval: 10s
+timeout: 30s # Riduce frequenza dei check
+```
+
+### 3. Retries Adeguati
+```yaml
+# ✓ GOOD - più retries per servizi lenti (database)
+healthcheck:
+ retries: 5
+
+# ✓ GOOD - meno retries per servizi veloci (web)
+healthcheck:
+ retries: 3
+```
+
+## Debugging
+
+### Testare manualmente
+```bash
+docker exec lab03-web wget --spider -q http://localhost/health
+echo $? # 0 = success, !0 = failure
+```
+
+### Vedere log healthcheck
+```bash
+docker inspect lab03-web --format '{{range .State.Health.Log}}{{.Output}} {{end}}'
+```
+
+### Reset health status
+```bash
+docker restart lab03-web
+```
+
+## Vedi Anche
+- Tutorial: Implementare Healthchecks
+- Reference: Healthcheck Syntax
diff --git a/labs/lab-03-compute/how-to-guides/instance-type-mapping.md b/labs/lab-03-compute/how-to-guides/instance-type-mapping.md
new file mode 100644
index 0000000..263608a
--- /dev/null
+++ b/labs/lab-03-compute/how-to-guides/instance-type-mapping.md
@@ -0,0 +1,97 @@
+# How-to: Selezionare l'EC2 Instance Type Corretto
+
+Come scegliere l'istanza EC2 giusta per il tuo workload Docker.
+
+## Decision Tree
+
+### Step 1: Tipo di Carico
+
+**Dev/Test:**
+- Usa `t2` (burstable) - economico
+- Config: `cpus: '0.5'`, `memory: 512M`
+
+**Web Server:**
+- Usa `t2.small` o `t2.medium`
+- Config: `cpus: '1'`, `memory: 2G` (o 4G)
+
+**Application Server:**
+- Usa `m5.large` - performance consistente
+- Config: `cpus: '2'`, `memory: 8G`
+
+**Database:**
+- Usa `t2.medium` (dev) o `m5.xlarge` (prod)
+- Config: `cpus: '2'`, `memory: 4G` (o 16G)
+
+**Batch Processing:**
+- Usa `c5` (compute optimized)
+- Config: `cpus: '4'`, `memory: 4G`
+
+### Step 2: Analizza il Tuo Container
+
+```bash
+# Monitora il consumo attuale
+docker stats lab03-app --no-stream
+
+# Se CPU > 70% → considera più CPU
+# Se Memory > 80% → considera più memoria
+```
+
+### Step 3: Considera il Costo
+
+| Instance | Costo/ora (us-east-1) | Use Case |
+|----------|----------------------|----------|
+| t2.nano | ~$0.006 | Micro |
+| t2.micro | ~$0.012 | Dev/Test |
+| t2.small | ~$0.024 | Web |
+| t2.medium | ~$0.048 | Application |
+| m5.large | ~$0.096 | Production |
+
+## Docker → EC2 Quick Reference
+
+```yaml
+# Dev/Test
+cpus: '0.5'; memory: 512M # → t2.nano
+cpus: '1'; memory: 1G # → t2.micro
+
+# Web Tier
+cpus: '1'; memory: 2G # → t2.small
+cpus: '2'; memory: 4G # → t2.medium
+
+# Application Tier
+cpus: '2'; memory: 8G # → m5.large
+cpus: '4'; memory: 16G # → m5.xlarge
+
+# Database Tier
+cpus: '2'; memory: 4G # → t2.medium (dev)
+cpus: '4'; memory: 32G # → m5.2xlarge (prod)
+```
+
+## Scaling Strategy
+
+### Horizontal Scaling
+```yaml
+# Più container piccoli (t2.micro)
+web:
+ deploy:
+ replicas: 4
+ resources:
+ limits:
+ cpus: '1'
+ memory: 1G
+```
+
+### Vertical Scaling
+```yaml
+# Meno container grandi (m5.large)
+web:
+ deploy:
+ replicas: 1
+ resources:
+ limits:
+ cpus: '2'
+ memory: 8G
+```
+
+## Vedi Anche
+- Reference: EC2 Instance Mapping
+- Explanation: Compute-EC2 Parallels
diff --git a/labs/lab-03-compute/how-to-guides/test-limits-enforcement.md b/labs/lab-03-compute/how-to-guides/test-limits-enforcement.md
new file mode 100644
index 0000000..00343af
--- /dev/null
+++ b/labs/lab-03-compute/how-to-guides/test-limits-enforcement.md
@@ -0,0 +1,88 @@
+# How-to: Testare l'Enforcement dei Limiti
+
+Come verificare che i limiti delle risorse siano effettivamente applicati.
+
+## Test 1: Verificare Configurazione
+
+### Controllare nel compose file
+
+```bash
+grep -A 10 "deploy:" docker-compose.yml | grep -E "cpus:|memory:"
+```
+
+### Controllare nel container
+
+```bash
+docker inspect lab03-web --format '{{.HostConfig.NanoCpus}}' # CPU (1e9 = 1 CPU)
+docker inspect lab03-web --format '{{.HostConfig.Memory}}' # Memory in bytes
+```
+
+## Test 2: Stress Test CPU
+
+### Avviare container con stress
+
+```bash
+docker run -d --name stress-test \
+ --cpus='0.5' \
+ polinux/stress \
+ --cpu 1 \
+ --timeout 30s
+```
+
+### Monitorare enforcement
+
+```bash
+docker stats stress-test --no-stream
+```
+
+**Risultato atteso:** CPU non supera il 50% (0.5 CPU)
+
+### Cleanup
+
+```bash
+docker rm -f stress-test
+```
+
+## Test 3: Stress Test Memory
+
+### Avviare test OOM
+
+```bash
+docker run -d --name mem-test \
+ --memory='512m' \
+ polinux/stress \
+ --vm 1 \
+ --vm-bytes 600M \
+ --timeout 60s
+```
+
+### Verificare OOM kill
+
+```bash
+docker ps -a --filter 'name=mem-test'
+```
+
+**Risultato atteso:** Container exited (code 137 = OOM killed)
+
+### Cleanup
+
+```bash
+docker rm -f mem-test
+```
+
+## Test 4: Verifica con Script
+
+```bash
+#!/bin/bash
+# verify-limits.sh
+
+for container in lab03-web lab03-app lab03-db; do
+ echo "Container: $container"
+ docker inspect "$container" --format ' CPUs: {{.HostConfig.NanoCpus}}'
+ docker inspect "$container" --format ' Memory: {{.HostConfig.Memory}}'
+done
+```
+
+## Vedi Anche
+- How-to: Check Resource Usage
+- Reference: EC2 Instance Mapping
diff --git a/labs/lab-03-compute/reference/compose-resources-syntax.md b/labs/lab-03-compute/reference/compose-resources-syntax.md
new file mode 100644
index 0000000..136465f
--- /dev/null
+++ b/labs/lab-03-compute/reference/compose-resources-syntax.md
@@ -0,0 +1,210 @@
+# Reference: Docker Compose Resources Syntax
+
+Riferimento completo per la configurazione delle risorse in Docker Compose.
+
+## Sezione deploy.resources
+
+### Struttura Completa
+
+```yaml
+services:
+ service_name:
+ deploy:
+ resources:
+ limits:
+ cpus: 'VALUE'
+ memory: 'VALUE'
+ reservations:
+ cpus: 'VALUE'
+ memory: 'VALUE'
+```
+
+## Parametri Limits
+
+### cpus
+
+**Formato:** Stringa con numero decimale
+
+```yaml
+cpus: '0.5' # 50% di 1 CPU
+cpus: '1' # 1 CPU completa
+cpus: '2' # 2 CPU complete
+cpus: '1.5' # 1.5 CPU
+cpus: '0.25' # 25% di 1 CPU
+```
+
+**Validità:**
+- Minimo: `0.001` (1 millesimo di CPU)
+- Massimo: Numero di CPU host (es. 8 su host con 8 core)
+- Default: Nessun limite (tutte le CPU disponibili)
+
+### memory
+
+**Formato:** Stringa con unità
+
+```yaml
+memory: '512M' # 512 Megabyte
+memory: '1G' # 1 Gigabyte
+memory: '128M' # 128 Megabyte
+memory: '4G' # 4 Gigabyte
+memory: '1024M' # 1024 Megabyte (equivale a 1G)
+```
+
+**Unità Supportate:**
+- `B` - Byte
+- `K` / `KB` - Kilobyte (1024 bytes)
+- `M` / `MB` - Megabyte (1024 KB)
+- `G` / `GB` - Gigabyte (1024 MB)
+
+**Validità:**
+- Minimo: `4M` (4 Megabyte)
+- Massico: Memoria host disponibile
+- Default: Nessun limite (tutta la memoria disponibile)
+
+## Parametri Reservations
+
+### Scopo
+
+Le **reservations** garantiscono risorse minime disponibili:
+- Docker riserva queste risorse per il container
+- Usato per pianificazione capacity (Kubernetes)
+- Meno comune in Docker Compose locale
+
+### Esempio
+
+```yaml
+deploy:
+ resources:
+ limits:
+ cpus: '2' # Massimo 2 CPU
+ memory: '4G' # Massimo 4 GB
+ reservations:
+ cpus: '1' # Garantite 1 CPU
+ memory: '2G' # Garantiti 2 GB
+```
+
+**Uso tipico:**
+- `limits` - Definisce il tetto massimo (enforcement)
+- `reservations` - Definisce il pavimento minimo (garanzia)
+
+## Esempi Completi per Servizi
+
+### Web Server (t2.micro)
+
+```yaml
+web:
+ image: nginx:alpine
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 1G
+```
+
+### Application Server (t2.small)
+
+```yaml
+app:
+ image: node:alpine
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 2G
+```
+
+### Database (t2.medium)
+
+```yaml
+db:
+ image: postgres:16-alpine
+ deploy:
+ resources:
+ limits:
+ cpus: '2'
+ memory: 4G
+ reservations:
+ cpus: '1'
+ memory: 2G
+```
+
+### Worker CPU-Intensive (c5.xlarge)
+
+```yaml
+worker:
+ image: python:alpine
+ deploy:
+ resources:
+ limits:
+ cpus: '4'
+ memory: 4G
+```
+
+## Validation
+
+### Verifica con docker compose config
+
+```bash
+docker compose config
+```
+
+Output per servizio con limits:
+```yaml
+services:
+ web:
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 1073741824 # 1G in byte
+```
+
+### Verifica con docker inspect
+
+```bash
+docker inspect lab03-web --format '{{.HostConfig}}'
+```
+
+Output:
+```
+map[NanoCpus:1000000000 Memory:1073741824]
+```
+
+Conversione:
+- `NanoCpus: 1000000000` = 1 CPU (1e9 nanocpus)
+- `Memory: 1073741824` = 1 GB (in byte)
+
+## Troubleshooting
+
+### Errore: "no matching resources"
+
+**Causa:** Host non ha abbastanza risorse
+
+**Soluzione:**
+```yaml
+# Riduci i limits
+deploy:
+ resources:
+ limits:
+ cpus: '0.5' # Era '2'
+ memory: 512M # Era '2G'
+```
+
+### Errore: "invalid memory format"
+
+**Causa:** Formato non valido
+
+**Soluzione:**
+```yaml
+# ✗ WRONG
+memory: 1024 # Manca unità
+memory: "1GB" # "GB" non valido (usa G)
+
+# ✓ CORRECT
+memory: 1G
+memory: 1024M
+```
+
+## Vedi Anche
+- Tutorial: Configurare Limiti delle Risorse
+- Reference: EC2 Instance Mapping
diff --git a/labs/lab-03-compute/reference/ec2-instance-mapping.md b/labs/lab-03-compute/reference/ec2-instance-mapping.md
new file mode 100644
index 0000000..607cc38
--- /dev/null
+++ b/labs/lab-03-compute/reference/ec2-instance-mapping.md
@@ -0,0 +1,159 @@
+# Reference: EC2 Instance Type Mapping
+
+Tabella completa di mapping tra Docker resource limits e EC2 instance types.
+
+## Tabella di Mapping Completa
+
+| Docker Compose | EC2 Instance | vCPUs | Memory | Costo/ora* | Use Case |
+|----------------|--------------|-------|--------|------------|----------|
+| `cpus: '0.5'`
`memory: 512M` | t2.nano | 0.5 | 512 MB | $0.006 | Microservizi minimi |
+| `cpus: '1'`
`memory: 1G` | t2.micro | 1 | 1 GB | $0.012 | Dev/Test |
+| `cpus: '1'`
`memory: 2G` | t2.small | 1 | 2 GB | $0.024 | Web server |
+| `cpus: '2'`
`memory: 4G` | t2.medium | 2 | 4 GB | $0.048 | Application |
+| `cpus: '2'`
`memory: 8G` | m5.large | 2 | 8 GB | $0.096 | Production |
+| `cpus: '4'`
`memory: 16G` | m5.xlarge | 4 | 16 GB | $0.192 | High traffic |
+| `cpus: '8'`
`memory: 32G` | m5.2xlarge | 8 | 32 GB | $0.384 | Scalabile |
+| `cpus: '16'`
`memory: 64G` | m5.4xlarge | 16 | 64 GB | $0.768 | Enterprise |
+
+\*Costi indicativi per regione us-east-1
+
+## Famiglie Instance Types
+
+### General Purpose (m5, m6)
+
+Equilibrato tra CPU, memoria, rete.
+
+```yaml
+# m5.large - 2 vCPUs, 8 GB
+cpus: '2'
+memory: 8G
+```
+
+**Use case:** Web server, application server
+
+### Burstable (t2, t3)
+
+Credit-based CPU, basso costo.
+
+```yaml
+# t2.micro - 1 vCPU, 1 GB
+cpus: '1'
+memory: 1G
+```
+
+**Use case:** Dev, test, microservizi
+
+### Compute Optimized (c5, c6)
+
+Alto ratio CPU/memoria.
+
+```yaml
+# c5.xlarge - 4 vCPUs, 8 GB
+cpus: '4'
+memory: 8G
+```
+
+**Use case:** Batch processing, encoding
+
+### Memory Optimized (r5, r6)
+
+Alto ratio memoria/CPU.
+
+```yaml
+# r5.large - 2 vCPUs, 16 GB
+cpus: '2'
+memory: 16G
+```
+
+**Use case:** Database, caching
+
+## Docker Compose Examples
+
+### T2 Nano (Micro)
+
+```yaml
+micro:
+ image: alpine:3.19
+ deploy:
+ resources:
+ limits:
+ cpus: '0.5'
+ memory: 512M
+```
+
+### T2 Micro (Dev/Test)
+
+```yaml
+dev:
+ image: node:alpine
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 1G
+```
+
+### T2 Small (Web)
+
+```yaml
+web:
+ image: nginx:alpine
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 2G
+```
+
+### T2 Medium (Application)
+
+```yaml
+app:
+ image: python:alpine
+ deploy:
+ resources:
+ limits:
+ cpus: '2'
+ memory: 4G
+```
+
+### M5 Large (Production)
+
+```yaml
+production:
+ image: myapp:latest
+ deploy:
+ resources:
+ limits:
+ cpus: '2'
+ memory: 8G
+```
+
+## Shortcut Reference
+
+### Quick Copy-Paste
+
+```yaml
+# T2 Nano
+cpus: '0.5'; memory: 512M
+
+# T2 Micro
+cpus: '1'; memory: 1G
+
+# T2 Small
+cpus: '1'; memory: 2G
+
+# T2 Medium
+cpus: '2'; memory: 4G
+
+# M5 Large
+cpus: '2'; memory: 8G
+
+# M5 XLarge
+cpus: '4'; memory: 16G
+```
+
+## Vedi Anche
+- Tutorial: Configurare Limiti delle Risorse
+- How-to: Selezionare Instance Type
+- Explanation: Compute-EC2 Parallels
diff --git a/labs/lab-03-compute/reference/healthcheck-syntax.md b/labs/lab-03-compute/reference/healthcheck-syntax.md
new file mode 100644
index 0000000..37e67d9
--- /dev/null
+++ b/labs/lab-03-compute/reference/healthcheck-syntax.md
@@ -0,0 +1,193 @@
+# Reference: Docker Compose Healthcheck Syntax
+
+Riferimento completo per la configurazione degli healthchecks.
+
+## Sezione healthcheck
+
+### Struttura Completa
+
+```yaml
+services:
+ service_name:
+ healthcheck:
+ test: ["CMD", "command", "arg1", "arg2"]
+ interval: 30s
+ timeout: 30s
+ retries: 3
+ start_period: 0s
+```
+
+## Parametri
+
+### test (Required)
+
+Comando da eseguire per verificare la salute.
+
+**Formato CMD:**
+```yaml
+test: ["CMD", "wget", "--spider", "-q", "http://localhost/health"]
+```
+
+**Formato CMD-SHELL:**
+```yaml
+test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
+```
+
+**Formato NONE:**
+```yaml
+test: ["NONE"]
+```
+Disabilita healthcheck ereditato dall'immagine.
+
+### interval
+
+Frequenza di esecuzione del test.
+
+```yaml
+interval: 5s # Ogni 5 secondi
+interval: 10s # Ogni 10 secondi
+interval: 30s # Ogni 30 secondi (default)
+interval: 1m # Ogni minuto
+```
+
+**Validità:**
+- Minimo: `1s` (1 secondo)
+- Massimo: Nessuno
+- Default: `30s`
+
+### timeout
+
+Tempo massimo per completare il test.
+
+```yaml
+timeout: 2s # 2 secondi
+timeout: 5s # 5 secondi (default)
+timeout: 30s # 30 secondi
+```
+
+**Nota:** Se il test supera il timeout, conta come fallimento.
+
+### retries
+
+Numero di fallimenti consecutivi prima di marcare unhealthy.
+
+```yaml
+retries: 1 # 1 fallimento = unhealthy
+retries: 3 # 3 fallimenti = unhealthy (default)
+retries: 5 # 5 fallimenti = unhealthy
+```
+
+**Calcolo del tempo unhealthy:**
+```
+tempo_totale = (interval + timeout) × retries
+```
+
+Esempio con `interval: 10s, timeout: 5s, retries: 3`:
+- Check 1: 0s - 5s
+- Check 2: 10s - 15s
+- Check 3: 20s - 25s
+- Unhealthy dopo: 25s
+
+### start_period
+
+Grace period prima di contare retries verso unhealthy.
+
+```yaml
+start_period: 0s # Nessun grace period (default)
+start_period: 5s # 5 secondi di grace
+start_period: 10s # 10 secondi di grace
+start_period: 30s # 30 secondi di grace
+```
+
+**Comportamento:**
+- Durante `start_period`: Fallimenti non contano
+- Dopo `start_period`: `retries` inizi a contare
+
+## Esempi per Tipo di Servizio
+
+### HTTP Service (Nginx)
+
+```yaml
+healthcheck:
+ test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
+ interval: 10s
+ timeout: 5s
+ retries: 3
+ start_period: 5s
+```
+
+### Database (PostgreSQL)
+
+```yaml
+healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U postgres || exit 1"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 10s
+```
+
+### Cache (Redis)
+
+```yaml
+healthcheck:
+ test: ["CMD", "redis-cli", "ping"]
+ interval: 10s
+ timeout: 3s
+ retries: 3
+```
+
+### TCP Connection
+
+```yaml
+healthcheck:
+ test: ["CMD-SHELL", "nc -z localhost 8080 || exit 1"]
+ interval: 15s
+ timeout: 2s
+ retries: 3
+```
+
+### File Check
+
+```yaml
+healthcheck:
+ test: ["CMD-SHELL", "test -f /tmp/ready || exit 1"]
+ interval: 5s
+ timeout: 1s
+ retries: 5
+```
+
+## Stati di Salute
+
+### Ciclo di Vita
+
+```
+created → starting → healthy
+ ↓
+ unhealthy
+```
+
+**Transizioni:**
+
+| Da | A | Condizione |
+|----|---|------------|
+| created | starting | Container avviato con healthcheck |
+| starting | healthy | `retries` consecutivi passano |
+| starting | unhealthy | `retries` consecutivi falliscono (dopo start_period) |
+| healthy | unhealthy | `retries` consecutivi falliscono |
+| unhealthy | healthy | 1 check passa |
+
+### Ispezionare Stato
+
+```bash
+# Stato corrente
+docker inspect lab03-web --format '{{.State.Health.Status}}'
+# Output: healthy / unhealthy / starting
+
+# Exit code dell'ultimo check
+docker inspect lab03-web --format '{{.State.Health.ExitCode}}'
+# Output: 0 (success) o !0 (failure)
+
+### Vedi Anche
+- Tutorial: Implementare Healthchecks
+- How-to: Custom Healthcheck
diff --git a/labs/lab-03-compute/tests/01-resource-limits-test.sh b/labs/lab-03-compute/tests/01-resource-limits-test.sh
new file mode 100755
index 0000000..1558c63
--- /dev/null
+++ b/labs/lab-03-compute/tests/01-resource-limits-test.sh
@@ -0,0 +1,215 @@
+#!/bin/bash
+# Test 01: Resource Limits Configuration
+# Verifies that docker-compose.yml services have mandatory resource limits
+# Usage: bash labs/lab-03-compute/tests/01-resource-limits-test.sh
+
+set -euo pipefail
+
+# Color definitions
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+BLUE='\033[0;34m'
+YELLOW='\033[1;33m'
+BOLD='\033[1m'
+NC='\033[0m'
+
+# Get script directory
+TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
+
+# Counter helpers
+pass_count=0
+fail_count=0
+
+inc_pass() { ((pass_count++)) || true; }
+inc_fail() { ((fail_count++)) || true; }
+
+# Helper functions
+print_header() {
+ echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
+ echo -e "${BLUE}║${NC} ${BOLD}$1${NC}"
+ echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
+}
+
+print_test() {
+ echo -e "\n${BLUE}[TEST]${NC} $1"
+}
+
+print_pass() {
+ echo -e " ${GREEN}[✓]${NC} $1"
+ inc_pass
+}
+
+print_fail() {
+ echo -e " ${RED}[✗]${NC} $1"
+ inc_fail
+}
+
+print_info() {
+ echo -e " ${BLUE}[i]${NC} $1"
+}
+
+# Main verification
+print_header "Lab 03 Resource Limits Verification"
+
+cd "$LAB_DIR"
+
+# Test 1: docker-compose.yml exists
+print_test "Verifying docker-compose.yml exists"
+if [[ -f "docker-compose.yml" ]]; then
+ print_pass "docker-compose.yml found"
+else
+ print_fail "docker-compose.yml not found"
+ exit 1
+fi
+
+# Test 2: docker-compose.yml is valid
+print_test "Validating docker-compose.yml syntax"
+if docker compose config &> /dev/null; then
+ print_pass "docker-compose.yml has valid syntax"
+else
+ print_fail "docker-compose.yml has syntax errors"
+ exit 1
+fi
+
+# Test 3: Services are defined
+print_test "Verifying services are defined"
+SERVICES=$(docker compose config --services 2>/dev/null)
+SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
+if [[ $SERVICE_COUNT -gt 0 ]]; then
+ print_pass "Found $SERVICE_COUNT services"
+ echo "$SERVICES" | while read -r service; do
+ print_info " - $service"
+ done
+else
+ print_fail "No services defined"
+ exit 1
+fi
+
+# Test 4: INF-03 compliance check - all services must have resource limits
+print_test "Checking INF-03 compliance (resource limits)"
+NON_COMPLIANT=0
+
+for service in $SERVICES; do
+ # Check for deploy.resources section
+ has_deploy=$(docker compose config 2>/dev/null | grep -A 20 "^$service:" | grep -c "deploy:" || echo "0")
+ has_resources=$(docker compose config 2>/dev/null | grep -A 25 "^$service:" | grep -c "resources:" || echo "0")
+
+ if [[ $has_deploy -eq 0 || $has_resources -eq 0 ]]; then
+ print_fail " $service: Missing deploy.resources section"
+ ((NON_COMPLIANT++)) || true
+ else
+ # Check for CPU limit
+ has_cpu=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "cpus:" || echo "0")
+ # Check for memory limit
+ has_memory=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "memory:" || echo "0")
+
+ if [[ $has_cpu -eq 0 ]]; then
+ print_fail " $service: Missing cpus limit"
+ ((NON_COMPLIANT++)) || true
+ fi
+
+ if [[ $has_memory -eq 0 ]]; then
+ print_fail " $service: Missing memory limit"
+ ((NON_COMPLIANT++)) || true
+ fi
+
+ if [[ $has_cpu -gt 0 && $has_memory -gt 0 ]]; then
+ # Extract limits for reporting
+ cpu_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "')
+ mem_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '")
+
+ print_pass " $service: cpus: $cpu_limit, memory: $mem_limit"
+ fi
+ fi
+done
+
+if [[ $NON_COMPLIANT -eq 0 ]]; then
+ print_pass "INF-03 compliance: All services have resource limits"
+else
+ print_fail "INF-03 compliance: $NON_COMPLIANT services missing limits"
+fi
+
+# Test 5: Verify specific EC2 instance type parallels
+print_test "Verifying EC2 instance type parallels"
+INSTANCE_TYPES=0
+
+for service in $SERVICES; do
+ cpu_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "' || echo "")
+ mem_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '"" || echo "")
+
+ # Map to EC2 instance types
+ if [[ -n "$cpu_limit" && -n "$mem_limit" ]]; then
+ case "$cpu_limit:$mem_limit" in
+ "0.5:512M"|"0.5:512")
+ instance_type="t2.nano"
+ ;;
+ "1:1G"|"1:1024M"|"1:1024")
+ instance_type="t2.micro"
+ ;;
+ "1:2G"|"1:2048M"|"1:2048")
+ instance_type="t2.small"
+ ;;
+ "2:4G"|"2:4096M"|"2:4096")
+ instance_type="t2.medium"
+ ;;
+ "2:8G"|"2:8192M"|"2:8192")
+ instance_type="m5.large"
+ ;;
+ *)
+ instance_type="custom"
+ ;;
+ esac
+
+ print_info " $service → $instance_type (cpus: $cpu_limit, memory: $mem_limit)"
+ ((INSTANCE_TYPES++)) || true
+ fi
+done
+
+if [[ $INSTANCE_TYPES -gt 0 ]]; then
+ print_pass "EC2 instance type mapping complete for $INSTANCE_TYPES services"
+else
+ print_fail "No services with valid resource limits found"
+fi
+
+# Test 6: Verify resource limit formats
+print_test "Verifying resource limit formats"
+FORMAT_OK=0
+
+for service in $SERVICES; do
+ cpu_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "' || echo "")
+
+ # CPU limit should be a number or decimal
+ if [[ -n "$cpu_limit" ]]; then
+ if [[ "$cpu_limit" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
+ ((FORMAT_OK++)) || true
+ else
+ print_fail " $service: Invalid CPU format: $cpu_limit"
+ fi
+ fi
+done
+
+if [[ $FORMAT_OK -eq $SERVICE_COUNT ]]; then
+ print_pass "All resource limits use valid format"
+else
+ print_fail "Some resource limits have invalid format"
+fi
+
+# Summary
+print_header "Resource Limits Verification Summary"
+
+echo -e "Tests run: $((pass_count + fail_count))"
+echo -e "${GREEN}Passed: $pass_count${NC}"
+if [[ $fail_count -gt 0 ]]; then
+ echo -e "${RED}Failed: $fail_count${NC}"
+fi
+
+if [[ $fail_count -eq 0 ]]; then
+ echo -e "\n${GREEN}${BOLD}✓ ALL RESOURCE LIMITS CHECKS PASSED${NC}"
+ echo -e "\nINF-03 compliance verified!"
+ exit 0
+else
+ echo -e "\n${RED}Some resource limits checks failed${NC}"
+ echo -e "Please ensure all services have cpus and memory limits."
+ exit 1
+fi
diff --git a/labs/lab-03-compute/tests/02-healthcheck-test.sh b/labs/lab-03-compute/tests/02-healthcheck-test.sh
new file mode 100755
index 0000000..255c3aa
--- /dev/null
+++ b/labs/lab-03-compute/tests/02-healthcheck-test.sh
@@ -0,0 +1,255 @@
+#!/bin/bash
+# Test 02: Healthcheck Configuration
+# Verifies that docker-compose.yml services have healthchecks configured
+# Usage: bash labs/lab-03-compute/tests/02-healthcheck-test.sh
+
+set -euo pipefail
+
+# Color definitions
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+BLUE='\033[0;34m'
+YELLOW='\033[1;33m'
+BOLD='\033[1m'
+NC='\033[0m'
+
+# Get script directory
+TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
+
+# Counter helpers
+pass_count=0
+fail_count=0
+
+inc_pass() { ((pass_count++)) || true; }
+inc_fail() { ((fail_count++)) || true; }
+
+# Helper functions
+print_header() {
+ echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
+ echo -e "${BLUE}║${NC} ${BOLD}$1${NC}"
+ echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
+}
+
+print_test() {
+ echo -e "\n${BLUE}[TEST]${NC} $1"
+}
+
+print_pass() {
+ echo -e " ${GREEN}[✓]${NC} $1"
+ inc_pass
+}
+
+print_fail() {
+ echo -e " ${RED}[✗]${NC} $1"
+ inc_fail
+}
+
+print_info() {
+ echo -e " ${BLUE}[i]${NC} $1"
+}
+
+# Main verification
+print_header "Lab 03 Healthcheck Verification"
+
+cd "$LAB_DIR"
+
+# Test 1: docker-compose.yml exists
+print_test "Verifying docker-compose.yml exists"
+if [[ -f "docker-compose.yml" ]]; then
+ print_pass "docker-compose.yml found"
+else
+ print_fail "docker-compose.yml not found"
+ exit 1
+fi
+
+# Test 2: docker-compose.yml is valid
+print_test "Validating docker-compose.yml syntax"
+if docker compose config &> /dev/null; then
+ print_pass "docker-compose.yml has valid syntax"
+else
+ print_fail "docker-compose.yml has syntax errors"
+ exit 1
+fi
+
+# Test 3: Services are defined
+print_test "Verifying services are defined"
+SERVICES=$(docker compose config --services 2>/dev/null)
+SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
+if [[ $SERVICE_COUNT -gt 0 ]]; then
+ print_pass "Found $SERVICE_COUNT services"
+else
+ print_fail "No services defined"
+ exit 1
+fi
+
+# Test 4: Healthcheck configuration check
+print_test "Checking healthcheck configuration"
+MISSING_HEALTHCHECK=0
+
+for service in $SERVICES; do
+ # Check for healthcheck section
+ has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
+
+ if [[ $has_healthcheck -eq 0 ]]; then
+ print_fail " $service: Missing healthcheck section"
+ ((MISSING_HEALTHCHECK++)) || true
+ else
+ # Verify healthcheck has test command
+ has_test=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep -c "test:" || echo "0")
+
+ if [[ $has_test -eq 0 ]]; then
+ print_fail " $service: Missing healthcheck test command"
+ ((MISSING_HEALTHCHECK++)) || true
+ else
+ # Extract test command for reporting
+ test_cmd=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "test:" | head -1 | sed 's/.*test: //' | tr -d '"')
+ print_pass " $service: healthcheck configured"
+ print_info " test: $test_cmd"
+ fi
+ fi
+done
+
+if [[ $MISSING_HEALTHCHECK -eq 0 ]]; then
+ print_pass "All services have healthchecks configured"
+else
+ print_fail "$MISSING_HEALTHCHECK services missing healthchecks"
+fi
+
+# Test 5: Healthcheck parameters verification
+print_test "Verifying healthcheck parameters"
+PARAMS_OK=0
+
+for service in $SERVICES; do
+ has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
+
+ if [[ $has_healthcheck -gt 0 ]]; then
+ # Check for interval
+ has_interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "interval:" || echo "0")
+ # Check for timeout
+ has_timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "timeout:" || echo "0")
+ # Check for retries
+ has_retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "retries:" || echo "0")
+
+ interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "interval:" | sed 's/.*interval: //' | tr -d " '"" || echo "N/A")
+ timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "timeout:" | sed 's/.*timeout: //' | tr -d " '"" || echo "N/A")
+ retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "retries:" | sed 's/.*retries: //' | tr -d " '"" || echo "N/A")
+
+ if [[ $has_interval -gt 0 && $has_timeout -gt 0 && $has_retries -gt 0 ]]; then
+ print_pass " $service: interval=$interval, timeout=$timeout, retries=$retries"
+ ((PARAMS_OK++)) || true
+ else
+ print_info " $service: interval=$interval, timeout=$timeout, retries=$retries"
+ # Check for start_period (optional)
+ has_start_period=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "start_period:" || echo "0")
+ if [[ $has_start_period -gt 0 ]]; then
+ start_period=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "start_period:" | sed 's/.*start_period: //' | tr -d " '"" || echo "")
+ print_info " start_period=$start_period"
+ fi
+ fi
+ fi
+done
+
+if [[ $PARAMS_OK -gt 0 ]]; then
+ print_pass "$PARAMS_OK services have complete healthcheck parameters"
+else
+ print_fail "No services with complete healthcheck parameters"
+fi
+
+# Test 6: Healthcheck command types
+print_test "Analyzing healthcheck command types"
+HTTP_COUNT=0
+CMD_SHELL_COUNT=0
+CMD_COUNT=0
+CUSTOM_COUNT=0
+
+for service in $SERVICES; do
+ has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
+
+ if [[ $has_healthcheck -gt 0 ]]; then
+ # Get test command
+ test_section=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" || echo "")
+
+ if echo "$test_section" | grep -q "CMD-SHELL"; then
+ print_info " $service: CMD-SHELL healthcheck"
+ ((CMD_SHELL_COUNT++)) || true
+ elif echo "$test_section" | grep -q "CMD"; then
+ print_info " $service: CMD healthcheck"
+ ((CMD_COUNT++)) || true
+ else
+ print_info " $service: Custom healthcheck format"
+ ((CUSTOM_COUNT++)) || true
+ fi
+
+ # Check if HTTP-based
+ if echo "$test_section" | grep -qiE "(wget|curl|http)"; then
+ ((HTTP_COUNT++)) || true
+ fi
+ fi
+done
+
+print_info "Healthcheck types: CMD-SHELL ($CMD_SHELL_COUNT), CMD ($CMD_COUNT), Custom ($CUSTOM_COUNT)"
+if [[ $HTTP_COUNT -gt 0 ]]; then
+ print_pass "$HTTP_COUNT services use HTTP-based healthchecks"
+fi
+
+# Test 7: ELB Health Check Parallel verification
+print_test "Verifying ELB Health Check parallelism"
+ELB_PARALLEL=0
+
+for service in $SERVICES; do
+ has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
+
+ if [[ $has_healthcheck -gt 0 ]]; then
+ interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "interval:" | sed 's/.*interval: //' | tr -d " '"" || echo "")
+ timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "timeout:" | sed 's/.*timeout: //' | tr -d " '"" || echo "")
+ retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "retries:" | sed 's/.*retries: //' | tr -d " '"" || echo "")
+
+ # Map to ELB defaults
+ if [[ "$interval" == "30s" || "$interval" =~ ^30 ]]; then
+ elb_interval="default (30s)"
+ ((ELB_PARALLEL++)) || true
+ else
+ elb_interval="custom ($interval)"
+ fi
+
+ if [[ "$timeout" == "5s" || "$timeout" =~ ^5 ]]; then
+ elb_timeout="default (5s)"
+ ((ELB_PARALLEL++)) || true
+ else
+ elb_timeout="custom ($timeout)"
+ fi
+
+ if [[ "$retries" == "3" || "$retries" =~ ^3 ]]; then
+ elb_retries="default (3)"
+ ((ELB_PARALLEL++)) || true
+ else
+ elb_retries="custom ($retries)"
+ fi
+
+ print_info " $service: ELB parallel (interval: $elb_interval, timeout: $elb_timeout, retries: $elb_retries)"
+ fi
+done
+
+if [[ $ELB_PARALLEL -gt 0 ]]; then
+ print_pass "ELB Health Check parallelism verified"
+fi
+
+# Summary
+print_header "Healthcheck Verification Summary"
+
+echo -e "Tests run: $((pass_count + fail_count))"
+echo -e "${GREEN}Passed: $pass_count${NC}"
+if [[ $fail_count -gt 0 ]]; then
+ echo -e "${RED}Failed: $fail_count${NC}"
+fi
+
+if [[ $fail_count -eq 0 ]]; then
+ echo -e "\n${GREEN}${BOLD}✓ ALL HEALTHCHECK CHECKS PASSED${NC}"
+ echo -e "\nHealthcheck configuration verified!"
+ exit 0
+else
+ echo -e "\n${RED}Some healthcheck checks failed${NC}"
+ echo -e "Please ensure all services have healthchecks configured."
+ exit 1
+fi
diff --git a/labs/lab-03-compute/tests/03-enforcement-test.sh b/labs/lab-03-compute/tests/03-enforcement-test.sh
new file mode 100755
index 0000000..74ac747
--- /dev/null
+++ b/labs/lab-03-compute/tests/03-enforcement-test.sh
@@ -0,0 +1,287 @@
+#!/bin/bash
+# Test 03: Resource Enforcement Verification
+# Verifies that resource limits are actually enforced using docker stats
+# Usage: bash labs/lab-03-compute/tests/03-enforcement-test.sh
+
+set -euo pipefail
+
+# Color definitions
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+BLUE='\033[0;34m'
+YELLOW='\033[1;33m'
+BOLD='\033[1m'
+NC='\033[0m'
+
+# Get script directory
+TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
+
+# Counter helpers
+pass_count=0
+fail_count=0
+
+inc_pass() { ((pass_count++)) || true; }
+inc_fail() { ((fail_count++)) || true; }
+
+# Helper functions
+print_header() {
+ echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
+ echo -e "${BLUE}║${NC} ${BOLD}$1${NC}"
+ echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
+}
+
+print_test() {
+ echo -e "\n${BLUE}[TEST]${NC} $1"
+}
+
+print_pass() {
+ echo -e " ${GREEN}[✓]${NC} $1"
+ inc_pass
+}
+
+print_fail() {
+ echo -e " ${RED}[✗]${NC} $1"
+ inc_fail
+}
+
+print_info() {
+ echo -e " ${BLUE}[i]${NC} $1"
+}
+
+print_warning() {
+ echo -e " ${YELLOW}[!]${NC} $1"
+}
+
+# Cleanup function
+cleanup() {
+ print_info "Cleaning up test containers..."
+ cd "$LAB_DIR"
+ docker compose down -v 2>/dev/null || true
+}
+
+# Set trap for cleanup
+trap cleanup EXIT
+
+# Main verification
+print_header "Lab 03 Resource Enforcement Verification"
+
+cd "$LAB_DIR"
+
+# Test 1: Verify docker-compose.yml exists and is valid
+print_test "Verifying docker-compose.yml exists and is valid"
+if [[ ! -f "docker-compose.yml" ]]; then
+ print_fail "docker-compose.yml not found"
+ exit 1
+fi
+
+if ! docker compose config &> /dev/null; then
+ print_fail "docker-compose.yml has syntax errors"
+ exit 1
+fi
+print_pass "docker-compose.yml is valid"
+
+# Test 2: Start services
+print_test "Starting Docker Compose services"
+if docker compose up -d &> /dev/null; then
+ print_pass "Services started successfully"
+ sleep 5 # Give services time to start
+else
+ print_fail "Failed to start services"
+ exit 1
+fi
+
+# Test 3: Verify containers are running
+print_test "Verifying containers are running"
+RUNNING_CONTAINERS=$(docker compose ps --services --filter "status=running" | wc -l)
+if [[ $RUNNING_CONTAINERS -ge 1 ]]; then
+ print_pass "Services running: $RUNNING_CONTAINERS containers"
+ docker compose ps
+else
+ print_fail "Not enough containers running"
+ exit 1
+fi
+
+# Test 4: Check resource limits are applied
+print_test "Verifying resource limits are applied to containers"
+LIMITS_APPLIED=0
+
+for service in $(docker compose config --services); do
+ container_name="lab03-$service"
+ if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
+ # Get CPU limit (NanoCPUs: 1e9 = 1 CPU core)
+ nano_cpus=$(docker inspect "$container_name" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
+ mem_limit=$(docker inspect "$container_name" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
+
+ # Convert NanoCPUs to CPU cores
+ if [[ $nano_cpus -gt 0 ]]; then
+ cpu_cores=$(echo "scale=2; $nano_cpus / 1000000000" | bc 2>/dev/null || echo "N/A")
+ else
+ cpu_cores="N/A"
+ fi
+
+ # Convert memory to GB/MB
+ if [[ $mem_limit -gt 0 ]]; then
+ if [[ $mem_limit -ge 1073741824 ]]; then
+ mem_gb=$(echo "scale=2; $mem_limit / 1073741824" | bc 2>/dev/null || echo "N/A")
+ mem_display="${mem_gb}G"
+ else
+ mem_mb=$(echo "scale=0; $mem_limit / 1048576" | bc 2>/dev/null || echo "N/A")
+ mem_display="${mem_mb}M"
+ fi
+ else
+ mem_display="N/A"
+ fi
+
+ if [[ "$cpu_cores" != "N/A" && "$mem_display" != "N/A" ]]; then
+ print_pass " $container_name: cpus: $cpu_cores, memory: $mem_display"
+ ((LIMITS_APPLIED++)) || true
+ else
+ print_fail " $container_name: Could not read limits (cpus: $cpu_cores, memory: $mem_display)"
+ fi
+ fi
+done
+
+if [[ $LIMITS_APPLIED -gt 0 ]]; then
+ print_pass "Resource limits applied to $LIMITS_APPLIED containers"
+else
+ print_fail "No containers with resource limits found"
+fi
+
+# Test 5: Monitor resource usage with docker stats
+print_test "Monitoring resource usage with docker stats"
+STATS_OK=0
+
+# Get initial stats
+print_info "Current resource usage:"
+docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}" 2>/dev/null || true
+
+for service in $(docker compose config --services); do
+ container_name="lab03-$service"
+ if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
+ # Get CPU percentage and memory usage
+ cpu_percent=$(docker stats "$container_name" --no-stream --format "{{.CPUPerc}}" 2>/dev/null | sed 's/%//' || echo "N/A")
+ mem_usage=$(docker stats "$container_name" --no-stream --format "{{.MemUsage}}" 2>/dev/null || echo "N/A")
+
+ if [[ "$cpu_percent" != "N/A" && "$mem_usage" != "N/A" ]]; then
+ print_info " $container_name: CPU: ${cpu_percent}%, Memory: $mem_usage"
+ ((STATS_OK++)) || true
+ fi
+ fi
+done
+
+if [[ $STATS_OK -gt 0 ]]; then
+ print_pass "docker stats successfully monitored $STATS_OK containers"
+else
+ print_fail "Failed to monitor container stats"
+fi
+
+# Test 6: Verify health status
+print_test "Verifying container health status"
+HEALTHY_COUNT=0
+
+for service in $(docker compose config --services); do
+ container_name="lab03-$service"
+ if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
+ # Get health status
+ health_status=$(docker inspect "$container_name" --format '{{.State.Health.Status}}' 2>/dev/null || echo "no-healthcheck")
+
+ if [[ "$health_status" == "healthy" ]]; then
+ print_pass " $container_name: $health_status"
+ ((HEALTHY_COUNT++)) || true
+ elif [[ "$health_status" == "no-healthcheck" ]]; then
+ print_info " $container_name: no healthcheck configured"
+ else
+ print_info " $container_name: $health_status"
+ fi
+ fi
+done
+
+if [[ $HEALTHY_COUNT -gt 0 ]]; then
+ print_pass "$HEALTHY_COUNT containers are healthy"
+else
+ print_warning "No healthy containers (services may still be starting)"
+fi
+
+# Test 7: Stress test for resource enforcement (if stress container exists)
+print_test "Stress testing resource enforcement (if stress container available)"
+
+STRESS_SERVICE=$(docker compose config --services | grep -E "(stress|test)" || echo "")
+if [[ -n "$STRESS_SERVICE" ]]; then
+ print_info "Found stress service: $STRESS_SERVICE"
+
+ # Get the container name
+ stress_container="lab03-$STRESS_SERVICE"
+ if docker ps --format '{{.Names}}' | grep -q "$stress_container"; then
+ # Get limits
+ nano_cpus=$(docker inspect "$stress_container" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
+ mem_limit=$(docker inspect "$stress_container" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
+
+ cpu_limit_cores=$(echo "scale=2; $nano_cpus / 1000000000" | bc 2>/dev/null || echo "N/A")
+ if [[ $mem_limit -ge 1073741824 ]]; then
+ mem_limit_display=$(echo "scale=2; $mem_limit / 1073741824" | bc 2>/dev/null || echo "N/A")"G"
+ else
+ mem_limit_display=$(echo "scale=0; $mem_limit / 1048576" | bc 2>/dev/null || echo "N/A")"M"
+ fi
+
+ print_info "Limits: cpus: $cpu_limit_cores, memory: $mem_limit_display"
+
+ # Monitor during stress test
+ print_info "Monitoring during stress (10 seconds)..."
+ for i in {1..5}; do
+ cpu_percent=$(docker stats "$stress_container" --no-stream --format "{{.CPUPerc}}" 2>/dev/null | sed 's/%//' || echo "N/A")
+ mem_usage=$(docker stats "$stress_container" --no-stream --format "{{.MemUsage}}" 2>/dev/null || echo "N/A")
+ print_info " Sample $i: CPU: ${cpu_percent}%, Memory: $mem_usage"
+ sleep 2
+ done
+
+ print_pass "Stress test completed"
+ fi
+else
+ print_info "No stress service found - skipping stress test"
+fi
+
+# Test 8: Verify resource limit enforcement
+print_test "Verifying resource limits are enforced"
+ENFORCED=0
+
+for service in $(docker compose config --services); do
+ container_name="lab03-$service"
+ if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
+ # Check if limits are set (not 0 or -1)
+ nano_cpus=$(docker inspect "$container_name" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
+ mem_limit=$(docker inspect "$container_name" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
+
+ if [[ $nano_cpus -gt 0 && $mem_limit -gt 0 ]]; then
+ print_pass " $container_name: Limits enforced (CPU and memory)"
+ ((ENFORCED++)) || true
+ else
+ print_fail " $container_name: Limits not enforced (CPU: $nano_cpus, Memory: $mem_limit)"
+ fi
+ fi
+done
+
+if [[ $ENFORCED -gt 0 ]]; then
+ print_pass "Resource limits enforced for $ENFORCED containers"
+else
+ print_fail "Resource limits not enforced for any container"
+fi
+
+# Summary
+print_header "Resource Enforcement Verification Summary"
+
+echo -e "Tests run: $((pass_count + fail_count))"
+echo -e "${GREEN}Passed: $pass_count${NC}"
+if [[ $fail_count -gt 0 ]]; then
+ echo -e "${RED}Failed: $fail_count${NC}"
+fi
+
+if [[ $fail_count -eq 0 ]]; then
+ echo -e "\n${GREEN}${BOLD}✓ ALL ENFORCEMENT CHECKS PASSED${NC}"
+ echo -e "\nResource limits are properly enforced!"
+ exit 0
+else
+ echo -e "\n${RED}Some enforcement checks failed${NC}"
+ echo -e "Please verify resource limit configuration."
+ exit 1
+fi
diff --git a/labs/lab-03-compute/tests/04-verify-infrastructure.sh b/labs/lab-03-compute/tests/04-verify-infrastructure.sh
new file mode 100755
index 0000000..baaa8bb
--- /dev/null
+++ b/labs/lab-03-compute/tests/04-verify-infrastructure.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+# Infrastructure Verification for Lab 03
+# Verifies docker-compose.yml is correctly deployed with resource limits and healthchecks
+
+set -euo pipefail
+
+RED='\033[0;31m'; GREEN='\033[0;32m'; BLUE='\033[0;34m'; YELLOW='\033[1;33m'; BOLD='\033[1m'; NC='\033[0m'
+pass_count=0; fail_count=0
+
+inc_pass() { ((pass_count++)) || true; }
+inc_fail() { ((fail_count++)) || true; }
+
+print_header() {
+ echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
+ echo -e "${BLUE}║${NC} ${BOLD}$1${NC}"
+ echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
+}
+
+print_test() { echo -e "\n${BLUE}[TEST]${NC} $1"; }
+print_pass() { echo -e " ${GREEN}[✓]${NC} $1"; inc_pass; }
+print_fail() { echo -e " ${RED}[✗]${NC} $1"; inc_fail; }
+
+cd "$(dirname "$0")/.."
+
+print_header "Lab 03 Infrastructure Verification"
+
+# File and syntax
+print_test "Verifying docker-compose.yml exists and valid"
+[[ -f "docker-compose.yml" ]] && print_pass "docker-compose.yml found" || { print_fail "docker-compose.yml not found"; exit 1; }
+docker compose config &> /dev/null && print_pass "Syntax valid" || { print_fail "Syntax error"; exit 1; }
+
+# Services and limits
+print_test "Checking INF-03 compliance (resource limits)"
+SERVICES=$(docker compose config --services 2>/dev/null)
+NON_COMPLIANT=0
+for s in $SERVICES; do
+ has_cpu=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep -c "cpus:" || echo "0")
+ has_mem=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep -c "memory:" || echo "0")
+ if [[ $has_cpu -eq 0 || $has_mem -eq 0 ]]; then
+ print_fail " $s: missing limits"
+ ((NON_COMPLIANT++)) || true
+ else
+ cpu_val=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "')
+ mem_val=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '""")
+ print_pass " $s: cpus=$cpu_val, memory=$mem_val"
+ fi
+done
+[[ $NON_COMPLIANT -eq 0 ]] && print_pass "INF-03 COMPLIANT" || print_fail "INF-03 VIOLATION: $NON_COMPLIANT services"
+
+# Healthchecks
+print_test "Verifying healthcheck configuration"
+MISSING_HC=0
+for s in $SERVICES; do
+ has_hc=$(docker compose config 2>/dev/null | grep -A50 "^$s:" | grep -c "healthcheck:" || echo "0")
+ [[ $has_hc -gt 0 ]] && print_pass " $s: healthcheck configured" || { print_fail " $s: missing healthcheck"; ((MISSING_HC++)); }
+done
+[[ $MISSING_HC -eq 0 ]] && print_pass "All services have healthchecks" || print_fail "$MISSING_HC missing healthchecks"
+
+# Deploy and verify
+print_test "Starting services"
+docker compose up -d &> /dev/null && print_pass "Services started" && sleep 8 || { print_fail "Failed to start"; exit 1; }
+
+print_test "Checking running containers"
+RUNNING=$(docker compose ps --services --filter "status=running" | wc -l)
+[[ $RUNNING -ge 4 ]] && print_pass "$RUNNING containers running" || print_fail "Only $RUNNING containers"
+
+# Enforcement
+print_test "Verifying resource limits enforced"
+ENFORCED=0
+for s in $SERVICES; do
+ c="lab03-$s"
+ if docker ps --format '{{.Names}}' | grep -q "$c"; then
+ nano_cpus=$(docker inspect "$c" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
+ mem_bytes=$(docker inspect "$c" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
+ [[ $nano_cpus -gt 0 && $mem_bytes -gt 0 ]] && print_pass " $c: limits enforced" || print_fail " $c: limits not applied"
+ [[ $nano_cpus -gt 0 && $mem_bytes -gt 0 ]] && ((ENFORCED++)) || true
+ fi
+done
+[[ $ENFORCED -gt 0 ]] && print_pass "Limits enforced for $ENFORCED containers"
+
+# Summary
+print_header "Summary"
+echo "Tests: $((pass_count + fail_count)) | ${GREEN}Passed: $pass_count${NC} ${RED}Failed: $fail_count${NC}"
+[[ $fail_count -eq 0 ]] && { echo -e "\n${GREEN}${BOLD}✓ ALL CHECKS PASSED${NC}\n"; exit 0; } || { echo -e "\n${RED}Some checks failed${NC}\n"; exit 1; }
diff --git a/labs/lab-03-compute/tests/99-final-verification.sh b/labs/lab-03-compute/tests/99-final-verification.sh
new file mode 100755
index 0000000..d9deba7
--- /dev/null
+++ b/labs/lab-03-compute/tests/99-final-verification.sh
@@ -0,0 +1,331 @@
+#!/bin/bash
+# Final Verification: Lab 03 - Compute & EC2
+# Comprehensive end-to-end verification for students
+# Usage: bash labs/lab-03-compute/tests/99-final-verification.sh
+
+set -euo pipefail
+
+# Color definitions
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+BLUE='\033[0;34m'
+YELLOW='\033[1;33m'
+BOLD='\033[1m'
+NC='\033[0m'
+
+# Get script directory
+TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
+
+# Counter helpers
+pass_count=0
+fail_count=0
+
+inc_pass() { ((pass_count++)) || true; }
+inc_fail() { ((fail_count++)) || true; }
+
+# Helper functions
+print_header() {
+ echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
+ echo -e "${BLUE}║${NC} ${BOLD}$1${NC}"
+ echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
+}
+
+print_test() {
+ echo -e "\n${BLUE}[TEST]${NC} $1"
+}
+
+print_pass() {
+ echo -e " ${GREEN}[✓]${NC} $1"
+ inc_pass
+}
+
+print_fail() {
+ echo -e " ${RED}[✗]${NC} $1"
+ inc_fail
+}
+
+print_info() {
+ echo -e " ${BLUE}[i]${NC} $1"
+}
+
+print_warning() {
+ echo -e " ${YELLOW}[!]${NC} $1"
+}
+
+# Cleanup function
+cleanup() {
+ print_info "\nCleaning up..."
+ cd "$LAB_DIR"
+ docker compose down -v 2>/dev/null || true
+}
+
+# Set trap for cleanup on exit
+trap cleanup EXIT
+
+# Main verification
+print_header "Lab 03 Final Verification - Compute & EC2"
+
+cd "$LAB_DIR"
+
+echo -e "\n${BLUE}This script verifies:${NC}"
+echo -e " • Resource limits (INF-03 compliance)"
+echo -e " • Healthcheck configuration"
+echo -e " • Resource enforcement with docker stats"
+echo -e " • EC2 instance type parallels"
+echo -e " • ELB health check parallels"
+
+# === SECTION 1: File and Syntax ===
+print_header "Section 1: Files and Syntax"
+
+# Test 1: docker-compose.yml exists
+print_test "Verifying docker-compose.yml exists"
+if [[ -f "docker-compose.yml" ]]; then
+ print_pass "docker-compose.yml found"
+else
+ print_fail "docker-compose.yml not found - create it first!"
+ exit 1
+fi
+
+# Test 2: Syntax validation
+print_test "Validating docker-compose.yml syntax"
+if docker compose config &> /dev/null; then
+ print_pass "docker-compose.yml syntax is valid"
+else
+ print_fail "docker-compose.yml has syntax errors - run 'docker compose config' to debug"
+ exit 1
+fi
+
+# Test 3: Services defined
+print_test "Checking defined services"
+SERVICES=$(docker compose config --services 2>/dev/null)
+SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
+if [[ $SERVICE_COUNT -ge 3 ]]; then
+ print_pass "Found $SERVICE_COUNT services (minimum 3 required)"
+else
+ print_fail "Only $SERVICE_COUNT services found (need at least 3)"
+ exit 1
+fi
+
+# === SECTION 2: INF-03 Compliance ===
+print_header "Section 2: INF-03 Compliance (Resource Limits)"
+
+print_test "Verifying all services have resource limits"
+NON_COMPLIANT=0
+LIMITS_SUMMARY=""
+
+for service in $SERVICES; do
+ # Check for resource limits
+ has_cpu=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "cpus:" || echo "0")
+ has_memory=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "memory:" || echo "0")
+
+ if [[ $has_cpu -eq 0 ]]; then
+ print_fail " $service: Missing cpus limit"
+ ((NON_COMPLIANT++)) || true
+ elif [[ $has_memory -eq 0 ]]; then
+ print_fail " $service: Missing memory limit"
+ ((NON_COMPLIANT++)) || true
+ else
+ cpu_val=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "')
+ mem_val=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '"")
+
+ # Map to EC2 instance type
+ case "$cpu_val:$mem_val" in
+ "0.5:512M"|"0.5:512") instance="t2.nano" ;;
+ "1:1G"|"1:1024M") instance="t2.micro" ;;
+ "1:2G"|"1:2048M") instance="t2.small" ;;
+ "2:4G"|"2:4096M") instance="t2.medium" ;;
+ "2:8G"|"2:8192M") instance="m5.large" ;;
+ "4:16G"|"4:16384M") instance="m5.xlarge" ;;
+ *) instance="custom" ;;
+ esac
+
+ print_pass " $service: cpus=$cpu_val, memory=$mem_val → $instance"
+ LIMITS_SUMMARY="$LIMITS_SUMMARY\n • $service → $instance"
+ fi
+done
+
+if [[ $NON_COMPLIANT -eq 0 ]]; then
+ print_pass "✓ INF-03 COMPLIANT: All services have resource limits"
+else
+ print_fail "✗ INF-03 VIOLATION: $NON_COMPLIANT services missing limits"
+fi
+
+# === SECTION 3: Healthcheck Configuration ===
+print_header "Section 3: Healthcheck Configuration"
+
+print_test "Verifying healthcheck configuration"
+MISSING_HC=0
+HC_SUMMARY=""
+
+for service in $SERVICES; do
+ has_hc=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
+
+ if [[ $has_hc -eq 0 ]]; then
+ print_fail " $service: Missing healthcheck"
+ ((MISSING_HC++)) || true
+ else
+ interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "interval:" | sed 's/.*interval: //' | tr -d " '"" || echo "N/A")
+ timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "timeout:" | sed 's/.*timeout: //' | tr -d " '"" || echo "N/A")
+ retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "retries:" | sed 's/.*retries: //' | tr -d " '"" || echo "N/A")
+
+ print_pass " $service: interval=$interval, timeout=$timeout, retries=$retries"
+ HC_SUMMARY="$HC_SUMMARY\n • $service: interval=$interval, timeout=$timeout, retries=$retries"
+ fi
+done
+
+if [[ $MISSING_HC -eq 0 ]]; then
+ print_pass "✓ All services have healthchecks configured"
+else
+ print_fail "✗ $MISSING_HC services missing healthchecks"
+fi
+
+# === SECTION 4: Infrastructure Deployment ===
+print_header "Section 4: Infrastructure Deployment"
+
+print_test "Starting services"
+if docker compose up -d &> /dev/null; then
+ print_pass "Services started successfully"
+ sleep 8 # Wait for services to be ready
+else
+ print_fail "Failed to start services"
+ exit 1
+fi
+
+print_test "Verifying containers are running"
+RUNNING=$(docker compose ps --services --filter "status=running" | wc -l)
+if [[ $RUNNING -ge 3 ]]; then
+ print_pass "$RUNNING containers running"
+ docker compose ps
+else
+ print_fail "Only $RUNNING containers running (expected 3+)"
+fi
+
+# === SECTION 5: Resource Enforcement ===
+print_header "Section 5: Resource Enforcement Verification"
+
+print_test "Checking resource limits are applied"
+LIMITS_ENFORCED=0
+
+for service in $SERVICES; do
+ container_name="lab03-$service"
+ if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
+ nano_cpus=$(docker inspect "$container_name" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
+ mem_bytes=$(docker inspect "$container_name" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
+
+ if [[ $nano_cpus -gt 0 && $mem_bytes -gt 0 ]]; then
+ cpu_cores=$(echo "scale=2; $nano_cpus / 1000000000" | bc 2>/dev/null || echo "N/A")
+ if [[ $mem_bytes -ge 1073741824 ]]; then
+ mem_display=$(echo "scale=2; $mem_bytes / 1073741824" | bc)G
+ else
+ mem_display=$(echo "scale=0; $mem_bytes / 1048576" | bc)M
+ fi
+ print_pass " $container_name: ${cpu_cores} CPU, $mem_display RAM"
+ ((LIMITS_ENFORCED++)) || true
+ else
+ print_fail " $container_name: Limits not applied"
+ fi
+ fi
+done
+
+if [[ $LIMITS_ENFORCED -gt 0 ]]; then
+ print_pass "✓ Resource limits enforced for $LIMITS_ENFORCED containers"
+else
+ print_fail "✗ Resource limits not enforced"
+fi
+
+# Test docker stats
+print_test "Monitoring resource usage"
+echo -e "\n${BLUE}Live Resource Usage:${NC}"
+docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}" 2>/dev/null || print_fail "docker stats failed"
+
+# === SECTION 6: Health Status ===
+print_header "Section 6: Health Status Verification"
+
+print_test "Checking container health status"
+HEALTHY=0
+
+for service in $SERVICES; do
+ container_name="lab03-$service"
+ if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
+ health=$(docker inspect "$container_name" --format '{{.State.Health.Status}}' 2>/dev/null || echo "no-healthcheck")
+
+ if [[ "$health" == "healthy" ]]; then
+ print_pass " $container_name: healthy ✓"
+ ((HEALTHY++)) || true
+ elif [[ "$health" == "no-healthcheck" ]]; then
+ print_warning " $container_name: no healthcheck configured"
+ else
+ print_info " $container_name: $health"
+ fi
+ fi
+done
+
+if [[ $HEALTHY -gt 0 ]]; then
+ print_pass "✓ $HEALTHY containers are healthy"
+else
+ print_warning "No healthy containers yet (may still be starting)"
+fi
+
+# === SECTION 7: Cloud Parallels ===
+print_header "Section 7: Cloud Parallels Verification"
+
+print_test "EC2 Instance Type Parallels"
+echo -e "${BLUE}Your Docker configuration maps to these EC2 instances:${NC}"
+echo -e "$LIMITS_SUMMARY"
+
+print_test "ELB Health Check Parallels"
+echo -e "${BLUE}Your healthchecks parallel ELB health checks:${NC}"
+echo -e "$HC_SUMMARY"
+
+# === FINAL SUMMARY ===
+print_header "Final Verification Summary"
+
+TOTAL_TESTS=$((pass_count + fail_count))
+PASS_PERCENT=$((pass_count * 100 / TOTAL_TESTS))
+
+echo -e "Total Tests: $TOTAL_TESTS"
+echo -e "${GREEN}Passed: $pass_count${NC} ($PASS_PERCENT%)"
+if [[ $fail_count -gt 0 ]]; then
+ echo -e "${RED}Failed: $fail_count${NC}"
+fi
+
+# INF-03 Compliance Summary
+echo -e "\n${BLUE}INF-03 Compliance:${NC}"
+if [[ $NON_COMPLIANT -eq 0 ]]; then
+ echo -e " ${GREEN}✓ COMPLIANT${NC} - All services have resource limits"
+else
+ echo -e " ${RED}✗ NON-COMPLIANT${NC} - $NON_COMPLIANT services missing limits"
+fi
+
+# Healthcheck Summary
+echo -e "\n${BLUE}Healthcheck Coverage:${NC}"
+if [[ $MISSING_HC -eq 0 ]]; then
+ echo -e " ${GREEN}✓ COMPLETE${NC} - All services have healthchecks"
+else
+ echo -e " ${RED}✗ INCOMPLETE${NC} - $MISSING_HC services missing healthchecks"
+fi
+
+# Final verdict
+if [[ $fail_count -eq 0 ]]; then
+ echo -e "\n${GREEN}${BOLD}╔═══════════════════════════════════════════════════════════════╗${NC}"
+ echo -e "${GREEN}${BOLD}║${NC} ${BOLD}✓✓✓ ALL VERIFICATIONS PASSED ✓✓✓${NC}"
+ echo -e "${GREEN}${BOLD}╚═══════════════════════════════════════════════════════════════╝${NC}"
+ echo -e "\n${GREEN}Congratulations! Your Lab 03 infrastructure is correctly deployed.${NC}"
+ echo -e "\nYou have successfully:"
+ echo -e " • Configured resource limits (EC2 instance types)"
+ echo -e " • Implemented healthchecks (ELB health checks)"
+ echo -e " • Verified resource enforcement with docker stats"
+ echo -e "\nYou can now proceed with the tutorials!"
+ exit 0
+else
+ echo -e "\n${RED}${BOLD}╔═══════════════════════════════════════════════════════════════╗${NC}"
+ echo -e "${RED}${BOLD}║${NC} ${BOLD}✗✗✗ SOME VERIFICATIONS FAILED ✗✗✗${NC}"
+ echo -e "${RED}${BOLD}╚═══════════════════════════════════════════════════════════════╝${NC}"
+ echo -e "\n${RED}Please review the failures above.${NC}"
+ echo -e "\nCommon issues:"
+ echo -e " • Missing resource limits → Add deploy.resources.limits to services"
+ echo -e " • Missing healthchecks → Add healthcheck section to services"
+ echo -e " • Services not starting → Check logs with 'docker compose logs'"
+ exit 1
+fi
diff --git a/labs/lab-03-compute/tests/quick-test.sh b/labs/lab-03-compute/tests/quick-test.sh
new file mode 100755
index 0000000..ab22d32
--- /dev/null
+++ b/labs/lab-03-compute/tests/quick-test.sh
@@ -0,0 +1,79 @@
+#!/bin/bash
+# Quick Test - Lab 03: Compute & EC2
+# Fast validation for development (< 30 seconds)
+# Usage: bash labs/lab-03-compute/tests/quick-test.sh
+
+set -euo pipefail
+
+# Color definitions
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+BLUE='\033[0;34m'
+YELLOW='\033[1;33m'
+BOLD='\033[1m'
+NC='\033[0m'
+
+# Get script directory
+TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
+
+# Quick checks (no verbose output)
+echo -e "${BLUE}Quick Test - Lab 03: Compute & EC2${NC}\n"
+
+# Check 1: File exists
+if [[ ! -f "$LAB_DIR/docker-compose.yml" ]]; then
+ echo -e "${RED}✗ docker-compose.yml not found${NC}"
+ exit 1
+fi
+echo -e "${GREEN}✓${NC} docker-compose.yml exists"
+
+# Check 2: Syntax valid
+if ! docker compose config &> /dev/null; then
+ echo -e "${RED}✗ docker-compose.yml syntax error${NC}"
+ exit 1
+fi
+echo -e "${GREEN}✓${NC} Syntax valid"
+
+# Check 3: Services defined
+SERVICES=$(docker compose config --services 2>/dev/null)
+SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
+if [[ $SERVICE_COUNT -lt 3 ]]; then
+ echo -e "${RED}✗ Only $SERVICE_COUNT services (need 3+)${NC}"
+ exit 1
+fi
+echo -e "${GREEN}✓${NC} $SERVICE_COUNT services defined"
+
+# Check 4: Resource limits (INF-03)
+NON_COMPLIANT=0
+for service in $SERVICES; do
+ has_cpu=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "cpus:" || echo "0")
+ has_mem=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "memory:" || echo "0")
+ if [[ $has_cpu -eq 0 || $has_mem -eq 0 ]]; then
+ ((NON_COMPLIANT++)) || true
+ fi
+done
+if [[ $NON_COMPLIANT -gt 0 ]]; then
+ echo -e "${RED}✗ $NON_COMPLIANT services missing resource limits (INF-03)${NC}"
+ exit 1
+fi
+echo -e "${GREEN}✓${NC} INF-03 compliant (all services have limits)"
+
+# Check 5: Healthchecks
+MISSING_HC=0
+for service in $SERVICES; do
+ has_hc=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
+ if [[ $has_hc -eq 0 ]]; then
+ ((MISSING_HC++)) || true
+ fi
+done
+if [[ $MISSING_HC -gt 0 ]]; then
+ echo -e "${YELLOW}⚠ $MISSING_HC services missing healthchecks${NC}"
+else
+ echo -e "${GREEN}✓${NC} All services have healthchecks"
+fi
+
+# Summary
+echo -e "\n${GREEN}${BOLD}✓ Quick test PASSED${NC}\n"
+echo -e "For full verification, run:"
+echo -e " bash tests/run-all-tests.sh"
+echo -e " bash tests/99-final-verification.sh"
diff --git a/labs/lab-03-compute/tests/run-all-tests.sh b/labs/lab-03-compute/tests/run-all-tests.sh
new file mode 100755
index 0000000..efb86ab
--- /dev/null
+++ b/labs/lab-03-compute/tests/run-all-tests.sh
@@ -0,0 +1,138 @@
+#!/bin/bash
+# Run All Tests - Lab 03: Compute & EC2
+# Executes all test scripts in sequence with fail-fast behavior
+# Usage: bash labs/lab-03-compute/tests/run-all-tests.sh
+
+set -euo pipefail
+
+# Color definitions
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+BLUE='\033[0;34m'
+YELLOW='\033[1;33m'
+BOLD='\033[1m'
+NC='\033[0m'
+
+# Get script directory
+TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
+
+# Counter helpers
+total_passed=0
+total_failed=0
+tests_run=0
+
+# Helper functions
+print_header() {
+ echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
+ echo -e "${BLUE}║${NC} ${BOLD}$1${NC}"
+ echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
+}
+
+print_section() {
+ echo -e "\n${BLUE}═══════════════════════════════════════════════════════════════${NC}"
+ echo -e "${BLUE} $1${NC}"
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}\n"
+}
+
+print_pass() {
+ echo -e "${GREEN}✓${NC} $1"
+}
+
+print_fail() {
+ echo -e "${RED}✗${NC} $1"
+}
+
+# Main execution
+print_header "Lab 03 Test Suite - Compute & EC2"
+
+cd "$LAB_DIR"
+
+echo -e "${BLUE}Running all tests in sequence...${NC}\n"
+
+# Test 1: Resource Limits Configuration
+print_section "Test 1/4: Resource Limits Configuration"
+if bash "$TEST_DIR/01-resource-limits-test.sh"; then
+ print_pass "Resource limits tests PASSED"
+ ((total_passed++)) || true
+else
+ print_fail "Resource limits tests FAILED"
+ ((total_failed++)) || true
+ echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
+ echo -e "Run individual tests to debug:"
+ echo -e " bash tests/01-resource-limits-test.sh"
+ exit 1
+fi
+((tests_run++)) || true
+
+# Test 2: Healthcheck Configuration
+print_section "Test 2/4: Healthcheck Configuration"
+if bash "$TEST_DIR/02-healthcheck-test.sh"; then
+ print_pass "Healthcheck tests PASSED"
+ ((total_passed++)) || true
+else
+ print_fail "Healthcheck tests FAILED"
+ ((total_failed++)) || true
+ echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
+ echo -e "Run individual tests to debug:"
+ echo -e " bash tests/02-healthcheck-test.sh"
+ exit 1
+fi
+((tests_run++)) || true
+
+# Test 3: Resource Enforcement
+print_section "Test 3/4: Resource Enforcement Verification"
+if bash "$TEST_DIR/03-enforcement-test.sh"; then
+ print_pass "Resource enforcement tests PASSED"
+ ((total_passed++)) || true
+else
+ print_fail "Resource enforcement tests FAILED"
+ ((total_failed++)) || true
+ echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
+ echo -e "Run individual tests to debug:"
+ echo -e " bash tests/03-enforcement-test.sh"
+ exit 1
+fi
+((tests_run++)) || true
+
+# Test 4: Final Verification
+print_section "Test 4/4: Final Verification"
+if bash "$TEST_DIR/99-final-verification.sh"; then
+ print_pass "Final verification PASSED"
+ ((total_passed++)) || true
+else
+ print_fail "Final verification FAILED"
+ ((total_failed++)) || true
+ echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
+ echo -e "Run individual tests to debug:"
+ echo -e " bash tests/99-final-verification.sh"
+ exit 1
+fi
+((tests_run++)) || true
+
+# Summary
+print_header "Test Suite Summary"
+
+echo -e "Tests run: $tests_run"
+echo -e "${GREEN}Passed: $total_passed${NC}"
+if [[ $total_failed -gt 0 ]]; then
+ echo -e "${RED}Failed: $total_failed${NC}"
+fi
+
+if [[ $total_failed -eq 0 ]]; then
+ echo -e "\n${GREEN}${BOLD}✓✓✓ ALL TESTS PASSED ✓✓✓${NC}\n"
+ echo -e "Your Lab 03 infrastructure is ready!"
+ echo -e "Proceed with the tutorials to learn more about:"
+ echo -e " • EC2 instance type parallels"
+ echo -e " • Resource limits enforcement"
+ echo -e " • Healthcheck implementation"
+ exit 0
+else
+ echo -e "\n${RED}Some tests failed${NC}\n"
+ echo -e "Run individual tests for details:"
+ echo -e " bash tests/01-resource-limits-test.sh"
+ echo -e " bash tests/02-healthcheck-test.sh"
+ echo -e " bash tests/03-enforcement-test.sh"
+ echo -e " bash tests/99-final-verification.sh"
+ exit 1
+fi
diff --git a/labs/lab-03-compute/tutorial/01-set-resource-limits.md b/labs/lab-03-compute/tutorial/01-set-resource-limits.md
new file mode 100644
index 0000000..f490f30
--- /dev/null
+++ b/labs/lab-03-compute/tutorial/01-set-resource-limits.md
@@ -0,0 +1,335 @@
+# Tutorial 1: Configurare i Limiti delle Risorse
+
+In questo tutorial imparerai a configurare i limiti delle risorse CPU e memoria per i container Docker, simulando i diversi **EC2 Instance Types** di AWS.
+
+## Obiettivi di Apprendimento
+
+Al termine di questo tutorial sarai in grado di:
+- Comprendere cosa sono gli **EC2 Instance Types** e come si applicano a Docker
+- Configurare i limiti **CPU** e **memoria** in Docker Compose
+- Mappare le configurazioni Docker alle istanze EC2
+- Verificare i limiti delle risorse con `docker stats`
+
+---
+
+## Prerequisiti
+
+- Docker Engine >= 24.0 installato e funzionante
+- Docker Compose V2 (`docker compose` comando disponibile)
+- Conoscenza base di Docker e docker-compose.yml
+
+---
+
+## Parte 1: EC2 Instance Types - Concetti Fondamentali
+
+### Cos'è un EC2 Instance Type?
+
+In AWS, un **Instance Type** definisce:
+- **vCPUs**: Numero di CPU virtuali
+- **Memory**: Quantità di RAM
+- **Use case**: Tipo di carico di lavoro ottimale
+
+### Famiglie di Instance Types Principali
+
+| Famiglia | Prefix | Use Case | Esempio |
+|----------|--------|----------|---------|
+| **Burstable** | t2, t3 | Dev/test, basso costo | t2.micro |
+| **General Purpose** | m5, m6 | Equilibrato CPU/memoria | m5.large |
+| **Compute Optimized** | c5, c6 | Alta CPU | c5.xlarge |
+| **Memory Optimized** | r5, r6 | Alta memoria | r5.large |
+
+### Instance Types Comuni
+
+| Instance Type | vCPUs | Memory | Use Case |
+|---------------|-------|--------|----------|
+| t2.nano | 0.5 | 512 MB | Microservizi |
+| t2.micro | 1 | 1 GB | Dev/Test |
+| t2.small | 1 | 2 GB | Web server |
+| t2.medium | 2 | 4 GB | Application server |
+| m5.large | 2 | 8 GB | Production app |
+| m5.xlarge | 4 | 16 GB | High traffic |
+
+---
+
+## Parte 2: Limiti delle Risorse in Docker Compose
+
+### Sintassi di Base
+
+In Docker Compose, i limiti delle risorse si configurano con la sezione `deploy.resources.limits`:
+
+```yaml
+version: "3.8"
+
+services:
+ app:
+ image: nginx:alpine
+ deploy:
+ resources:
+ limits:
+ cpus: '1' # Numero di CPU
+ memory: 1G # Memoria (M, G)
+```
+
+### Formati della Memoria
+
+```yaml
+memory: 512M # 512 Megabyte
+memory: 1G # 1 Gigabyte
+memory: 1024M # 1024 Megabyte (equivale a 1G)
+```
+
+### Format delle CPU
+
+```yaml
+cpus: '0.5' # Mezza CPU
+cpus: '1' # 1 CPU completa
+cpus: '2' # 2 CPU complete
+cpus: '1.5' # 1.5 CPU (1 completa + 50% di un'altra)
+```
+
+---
+
+## Parte 3: Pratica - Configurare un Container t2.micro
+
+Creiamo un container simile a un'istanza **t2.micro** (1 vCPU, 1 GB RAM).
+
+### Step 1: Creare il file docker-compose.yml
+
+Crea il file `labs/lab-03-compute/docker-compose.yml`:
+
+```yaml
+version: "3.8"
+
+services:
+ # Web server - simula t2.micro
+ web:
+ image: nginx:alpine
+ container_name: lab03-web
+ hostname: web
+
+ # Resource limits - t2.micro parallel
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 1G
+
+ ports:
+ - "127.0.0.1:8080:80"
+
+ restart: unless-stopped
+```
+
+### Step 2: Verificare la sintassi
+
+```bash
+cd labs/lab-03-compute
+docker compose config
+```
+
+Se non ci sono errori, vedrai la configurazione completa.
+
+### Step 3: Avviare il container
+
+```bash
+docker compose up -d
+```
+
+### Step 4: Verificare i limiti applicati
+
+```bash
+# Ispeziona il container per vedere i limiti
+docker inspect lab03-web --format '{{.HostConfig.NanoCpus}}'
+# Output: 1000000000 (1e9 = 1 CPU)
+
+docker inspect lab03-web --format '{{.HostConfig.Memory}}'
+# Output: 1073741824 (1 GB in byte)
+```
+
+### Step 5: Monitorare l'utilizzo delle risorse
+
+```bash
+# Mostra l'utilizzo in tempo reale
+docker stats lab03-web
+```
+
+Premi `Ctrl+C` per uscire.
+
+```bash
+# Snapshot singolo
+docker stats --no-stream
+```
+
+**Output previsto:**
+```
+CONTAINER NAME CPU % MEM USAGE / LIMIT MEM %
+12345 lab03-web 0.01% 2.5MiB / 1GiB 0.24%
+```
+
+Nota che il **LIMIT** è 1GiB, configurato correttamente.
+
+---
+
+## Parte 4: Pratica - Configurare un Container t2.small
+
+Ora configuriamo un container simile a **t2.small** (1 vCPU, 2 GB RAM).
+
+### Aggiungi il servizio app al docker-compose.yml:
+
+```yaml
+ # Application server - simula t2.small
+ app:
+ image: nginx:alpine
+ container_name: lab03-app
+ hostname: app
+
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 2G
+
+ ports:
+ - "127.0.0.1:8081:80"
+
+ restart: unless-stopped
+```
+
+### Avviare e verificare
+
+```bash
+docker compose up -d
+docker compose ps
+```
+
+```bash
+# Verifica i limiti
+docker inspect lab03-app --format 'CPU: {{.HostConfig.NanoCpus}} CPUs, Memory: {{.HostConfig.Memory}} bytes'
+```
+
+---
+
+## Parte 5: Pratica - Configurare un Container t2.medium
+
+Configuriamo un container **t2.medium** (2 vCPU, 4 GB RAM).
+
+### Aggiungi il servizio worker:
+
+```yaml
+ # Worker - simula t2.medium
+ worker:
+ image: alpine:3.19
+ container_name: lab03-worker
+ hostname: worker
+
+ command: ["sh", "-c", "sleep 3600"]
+
+ deploy:
+ resources:
+ limits:
+ cpus: '2'
+ memory: 4G
+
+ restart: unless-stopped
+```
+
+### Avviare e verificare
+
+```bash
+docker compose up -d
+docker stats --no-stream lab03-worker
+```
+
+---
+
+## Parte 6: Tabella di Mapping Completa
+
+Ecco la tabella completa di mapping tra Docker e EC2:
+
+| Docker Limits | EC2 Instance | vCPUs | Memory | Use Case |
+|---------------|--------------|-------|--------|----------|
+| `cpus: '0.5'`
`memory: 512M` | t2.nano | 0.5 | 512 MB | Microservizi minimi |
+| `cpus: '1'`
`memory: 1G` | t2.micro | 1 | 1 GB | Dev/Test |
+| `cpus: '1'`
`memory: 2G` | t2.small | 1 | 2 GB | Web servers |
+| `cpus: '2'`
`memory: 4G` | t2.medium | 2 | 4 GB | Application |
+| `cpus: '2'`
`memory: 8G` | m5.large | 2 | 8 GB | Production |
+| `cpus: '4'`
`memory: 16G` | m5.xlarge | 4 | 16 GB | High traffic |
+
+---
+
+## Parte 7: Verifica Finale
+
+### Script di Verifica
+
+Esegui questo comando per verificare tutti i limiti:
+
+```bash
+# Per ogni servizio, mostra i limiti
+for service in web app worker; do
+ echo "Service: $service"
+ docker inspect "lab03-$service" --format ' CPUs: {{.HostConfig.NanoCpus}}'
+ docker inspect "lab03-$service" --format ' Memory: {{.HostConfig.Memory}}'
+done
+```
+
+### Controllo INF-03
+
+Lo script di verifica del lab controllerà automaticamente che tutti i servizi abbiano i limiti configurati (requisito **INF-03**).
+
+```bash
+bash tests/01-resource-limits-test.sh
+```
+
+---
+
+## Risoluzione Problemi
+
+### Errore: "no matching resources"
+
+**Causa:** I limiti specificati superano le risorse disponibili sull'host.
+
+**Soluzione:**
+- Riduci i limiti CPU/memoria
+- Libera risorse sull'host
+- Usa un'istanza tipo più piccola
+
+### Errore: "invalid memory format"
+
+**Causa:** Formato della memoria non valido.
+
+**Soluzione:**
+- Usa `M` per Megabyte (es. `512M`)
+- Usa `G` per Gigabyte (es. `1G`)
+- Non usare spazi o formati misti
+
+### Container OOM Killed
+
+**Causa:** Il container sta tentando di usare più memoria del limite.
+
+**Soluzione:**
+- Aumenta il limite `memory`
+- Indaga il consumo di memoria dell'applicazione
+- Verifica memory leak
+
+---
+
+## Riepilogo
+
+In questo tutorial hai imparato:
+
+✓ **Concetto:** EC2 Instance Types definiscono CPU e memoria
+✓ **Sintassi:** `deploy.resources.limits` in Docker Compose
+✓ **Mapping:** Docker limits → EC2 instances
+✓ **Verifica:** `docker stats` per monitorare l'utilizzo
+✓ **Compliance:** INF-03 richiede limiti per tutti i container
+
+---
+
+## Prossimi Passi
+
+Nel prossimo tutorial imparerai a:
+- Implementare **healthchecks** per monitorare lo stato dei servizi
+- Configurare dipendenze tra servizi con `depends_on`
+- Mappare healthchecks Docker agli **ELB Health Checks** di AWS
+
+Continua con **Tutorial 2: Implementare Healthchecks** →
diff --git a/labs/lab-03-compute/tutorial/02-implement-healthchecks.md b/labs/lab-03-compute/tutorial/02-implement-healthchecks.md
new file mode 100644
index 0000000..af4cb92
--- /dev/null
+++ b/labs/lab-03-compute/tutorial/02-implement-healthchecks.md
@@ -0,0 +1,347 @@
+# Tutorial 2: Implementare Healthchecks
+
+In questo tutorial imparerai a configurare healthchecks per i container Docker, simulando gli **ELB Health Checks** di AWS.
+
+## Obiettivi di Apprendimento
+
+Al termine di questo tutorial sarai in grado di:
+- Comprendere cosa sono i healthchecks e perché sono importanti
+- Configurare healthchecks HTTP, CMD e custom
+- Mappare healthchecks Docker agli ELB Health Checks
+- Monitorare lo stato di salute dei container
+
+---
+
+## Prerequisiti
+
+- Completamento di Tutorial 1: Configurare i Limiti delle Risorse
+- docker-compose.yml con servizi configurati
+- Container che espongono porte HTTP o servizi monitorabili
+
+---
+
+## Parte 1: Healthchecks - Concetti Fondamentali
+
+### Cos'è un Healthcheck?
+
+Un **healthcheck** è un comando periodico che verifica se un container è "sano" (healthy).
+
+**Stati di un Container:**
+1. **created** - Container creato ma non avviato
+2. **starting** - Container in avvio (healthcheck in corso)
+3. **healthy** - Healthcheck passing (container OK)
+4. **unhealthy** - Healthcheck failing (container problematico)
+5. **exited** - Container terminato
+
+### Perché sono Importanti?
+
+- **Failover:** Sostituisce container non sani
+- **Zero-downtime:** Attiva solo container sani nel load balancer
+- **Dependencies:** Altri servizi aspettano che il container diventi healthy
+- **Monitoring:** Avvisa automaticamente su problemi
+
+### Parallelismo: ELB Health Checks
+
+| Docker | AWS ELB |
+|--------|---------|
+| healthcheck.test | Health check path/protocol |
+| healthcheck.interval | Health check interval (default 30s) |
+| healthcheck.timeout | Health check timeout (default 5s) |
+| healthcheck.retries | Unhealthy threshold |
+| healthcheck.start_period | Grace period |
+
+---
+
+## Parte 2: Sintassi Healthcheck in Docker Compose
+
+### Sintassi di Base
+
+```yaml
+services:
+ web:
+ image: nginx:alpine
+ healthcheck:
+ test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
+ interval: 10s
+ timeout: 5s
+ retries: 3
+ start_period: 5s
+```
+
+### Parametri Spiegati
+
+| Parametro | Default | Descrizione |
+|-----------|---------|-------------|
+| test | - | Comando da eseguire (richiesto) |
+| interval | 30s | Frequenza del check |
+| timeout | 30s | Tempo massimo per completare |
+| retries | 3 | Tentativi prima di标记 unhealthy |
+| start_period | 0s | Grace period all'avvio |
+
+---
+
+## Parte 3: Pratica - HTTP Healthcheck per Web Server
+
+### Step 1: Aggiungere healthcheck al servizio web
+
+Modifica `docker-compose.yml`:
+
+```yaml
+ web:
+ image: nginx:alpine
+ container_name: lab03-web
+ hostname: web
+
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 1G
+
+ # HTTP Healthcheck
+ healthcheck:
+ test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
+ interval: 10s
+ timeout: 5s
+ retries: 3
+ start_period: 5s
+
+ ports:
+ - "127.0.0.1:8080:80"
+
+ restart: unless-stopped
+```
+
+### Step 2: Avviare e verificare
+
+```bash
+docker compose up -d web
+```
+
+### Step 3: Monitorare lo stato di salute
+
+```bash
+# Mostra stato di salute
+docker ps
+
+# Output:
+# CONTAINER IMAGE STATUS
+# lab03-web nginx:alpine Up 30 seconds (healthy)
+```
+
+### Step 4: Ispezionare i dettagli del healthcheck
+
+```bash
+docker inspect lab03-web --format '{{json .State.Health}}' | jq
+```
+
+**Output JSON:**
+```json
+{
+ "Status": "healthy",
+ "FailingStreak": 0,
+ "Log": [
+ {
+ "Start": "2024-03-25T10:00:00Z",
+ "End": "2024-03-25T10:00:00Z",
+ "ExitCode": 0,
+ "Output": ""
+ }
+ ]
+}
+```
+
+---
+
+## Parte 4: Pratica - Database Healthcheck
+
+### Step 1: Aggiungere servizio database
+
+```yaml
+ db:
+ image: postgres:16-alpine
+ container_name: lab03-db
+ hostname: db
+
+ environment:
+ POSTGRES_DB: lab03_db
+ POSTGRES_USER: lab03_user
+ POSTGRES_PASSWORD: lab03_password
+
+ deploy:
+ resources:
+ limits:
+ cpus: '2'
+ memory: 4G
+
+ # Database Healthcheck
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U lab03_user -d lab03_db || exit 1"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 10s
+
+ volumes:
+ - db-data:/var/lib/postgresql/data
+
+ restart: unless-stopped
+```
+
+Nota che il database:
+- Ha più `retries` (5 vs 3) - i database partono più lentamente
+- Ha `start_period` più lungo (10s vs 5s) - grace period esteso
+
+### Step 2: Verificare il database diventi healthy
+
+```bash
+docker compose up -d db
+
+# Attendere che diventi healthy
+watch -n 2 'docker ps --filter "name=lab03-db" --format "table {{.Names}}\t{{.Status}}"'
+```
+
+Premi `Ctrl+C` quando vedi `(healthy)`.
+
+---
+
+## Parte 5: Pratica - CMD-SHELL Healthcheck
+
+Per comandi più complessi, usa `CMD-SHELL`:
+
+```yaml
+ app:
+ image: myapp:latest
+ container_name: lab03-app
+ hostname: app
+
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 2G
+
+ # Custom healthcheck with shell
+ healthcheck:
+ test: ["CMD-SHELL", "curl -f http://localhost/health || exit 1"]
+ interval: 15s
+ timeout: 3s
+ retries: 3
+ start_period: 30s
+
+ restart: unless-stopped
+```
+
+### Esempi di Healthcheck
+
+**HTTP con curl:**
+```yaml
+test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
+```
+
+**TCP connection:**
+```yaml
+test: ["CMD-SHELL", "nc -z localhost 8080 || exit 1"]
+```
+
+**File existence:**
+```yaml
+test: ["CMD-SHELL", "test -f /tmp/ready || exit 1"]
+```
+
+**Simple always-succeed:**
+```yaml
+test: ["CMD-SHELL", "exit 0"]
+```
+
+---
+
+## Parte 6: ELB Health Check Parallelism
+
+### Mapping Completo
+
+| Docker Healthcheck | ELB Health Check | AWS Default | Docker Default |
+|--------------------|------------------|-------------|----------------|
+| test | Protocol + Path | TCP:80 | Configurato |
+| interval | Interval | 30s | 30s |
+| timeout | Timeout | 5s | 30s |
+| retries | Unhealthy Threshold | 2 | 3 |
+| start_period | - | - | 0s |
+
+### Configurazione Equivalente AWS
+
+**Docker:**
+```yaml
+healthcheck:
+ test: ["CMD", "wget", "--spider", "-q", "http://localhost/health"]
+ interval: 30s
+ timeout: 5s
+ retries: 2
+```
+
+**ELB (equivalente):**
+```json
+{
+ "TargetGroup": {
+ "HealthCheckProtocol": "HTTP",
+ "HealthCheckPath": "/health",
+ "HealthCheckIntervalSeconds": 30,
+ "HealthCheckTimeoutSeconds": 5,
+ "UnhealthyThresholdCount": 2
+ }
+}
+```
+
+---
+
+## Parte 7: Debugging Healthchecks
+
+### Problema: Container rimane "starting"
+
+**Causa:** Healthcheck non passa entro `start_period`
+
+**Soluzione:**
+- Aumenta `start_period`
+- Riduci `interval` per check più frequenti
+- Verifica che il servizio sia realmente pronto
+
+### Problema: Container diventa "unhealthy"
+
+**Debug:**
+```bash
+# Guarda i log del healthcheck
+docker inspect lab03-web --format '{{range .State.Health.Log}}{{.Start}} - {{.ExitCode}} - {{.Output}}{{"\n"}}{{end}}'
+
+# Esegui il comando manualmente
+docker exec lab03-web wget --spider -q http://localhost/
+```
+
+### Problema: Healthcheck troppo lento
+
+**Ottimizza:**
+- Usa check HTTP leggeri (non pagine pesanti)
+- Usa check TCP invece di HTTP se possibile
+- Riduci `timeout` se il check è veloce
+
+---
+
+## Riepilogo
+
+In questo tutorial hai imparato:
+
+✓ **Concetto:** Healthchecks verificano lo stato di salute dei container
+✓ **Sintassi:** Parametri test, interval, timeout, retries, start_period
+✓ **Tipi:** HTTP, CMD-SHELL, custom healthchecks
+✓ **Parallelismo:** Healthcheck Docker → ELB Health Check
+✓ **Monitoring:** `docker ps` mostra stato (healthy/unhealthy)
+
+---
+
+## Prossimi Passi
+
+Nel prossimo tutorial imparerai a:
+- Configurare **dipendenze** tra servizi
+- Usare `depends_on` con `condition: service_healthy`
+- Implementare startup ordinato per applicazioni multi-tier
+
+Continua con **Tutorial 3: Dipendenze con Healthchecks** →
diff --git a/labs/lab-03-compute/tutorial/03-dependencies-with-health.md b/labs/lab-03-compute/tutorial/03-dependencies-with-health.md
new file mode 100644
index 0000000..7c99a3c
--- /dev/null
+++ b/labs/lab-03-compute/tutorial/03-dependencies-with-health.md
@@ -0,0 +1,410 @@
+# Tutorial 3: Dipendenze tra Servizi con Healthchecks
+
+In questo tutorial imparerai a configurare dipendenze tra servizi usando `depends_on`, assicurando che i servizi partano nell'ordine corretto.
+
+## Obiettivi di Apprendimento
+
+Al termine di questo tutorial sarai in grado di:
+- Comprendere le dipendenze tra servizi in applicazioni multi-tier
+- Configurare `depends_on` con conditions
+- Implementare startup ordinato con healthchecks
+- Verificare l'ordine di avvio dei servizi
+
+---
+
+## Prerequisiti
+
+- Completamento di Tutorial 1 e 2
+- Servizi configurati con resource limits e healthchecks
+- Comprensione base di architetture multi-tier
+
+---
+
+## Parte 1: Dipendenze tra Servizi
+
+### Cos'è una Dipendenza?
+
+In un'applicazione multi-tier, i servizi dipendono l'uno dall'altro:
+
+```
+Web → App → Database
+```
+
+- Il **Web** server ha bisogno dell'**App** server
+- L'**App** server ha bisogno del **Database**
+- Il **Database** non dipende da nessuno (parte prima)
+
+### Perché sono Importanti?
+
+1. **Prevenzione Errori:** Evita connection error a servizi non pronti
+2. **Startup Affidabile:** Ogni servizio aspetta che le sue dipendenze siano pronte
+3. **Zero-Downtime:** Durante deployment, i nuovi servizi aspettano i vecchi
+4. **Debugging:** Problemi di connessione sono più facili da diagnosticare
+
+---
+
+## Parte 2: Tipi di Dipendenze
+
+### depends_on: service_started (Default)
+
+```yaml
+services:
+ app:
+ depends_on:
+ - db
+```
+
+**Comportamento:** `app` parte quando `db` è **avviato** (non necessariamente healthy)
+
+**Problema:** Il database potrebbe ancora essere inizializzando → connection errors
+
+### depends_on: service_healthy (Raccomandato)
+
+```yaml
+services:
+ app:
+ depends_on:
+ db:
+ condition: service_healthy
+```
+
+**Comportamento:** `app` parte quando `db` è **healthy** (dopo healthcheck pass)
+
+**Vantaggio:** Il database è completamente pronto → nessun connection error
+
+---
+
+## Parte 3: Pratica - Architettura Multi-Tier
+
+Creiamo un'applicazione a 3 tier con dipendenze:
+
+```
+Web (t2.micro) → App (t2.small) → DB (t2.medium)
+```
+
+### Step 1: Configurare il Database (Primo)
+
+```yaml
+version: "3.8"
+
+services:
+ # Tier 3: Database - nessuna dipendenza
+ db:
+ image: postgres:16-alpine
+ container_name: lab03-db
+ hostname: db
+
+ environment:
+ POSTGRES_DB: lab03_db
+ POSTGRES_USER: lab03_user
+ POSTGRES_PASSWORD: lab03_password
+
+ deploy:
+ resources:
+ limits:
+ cpus: '2'
+ memory: 4G
+
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U lab03_user -d lab03_db || exit 1"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 10s
+
+ volumes:
+ - db-data:/var/lib/postgresql/data
+
+ restart: unless-stopped
+
+volumes:
+ db-data:
+```
+
+**Nota:** Il database non ha `depends_on` - è il primo a partire.
+
+### Step 2: Configurare l'App Server
+
+```yaml
+ # Tier 2: Application - dipende dal database
+ app:
+ image: nginx:alpine
+ container_name: lab03-app
+ hostname: app
+
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 2G
+
+ healthcheck:
+ test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
+ interval: 10s
+ timeout: 5s
+ retries: 3
+ start_period: 5s
+
+ ports:
+ - "127.0.0.1:8081:80"
+
+ # Dipende dal database healthy
+ depends_on:
+ db:
+ condition: service_healthy
+
+ restart: unless-stopped
+```
+
+**Nota:** `app` parte SOLO quando `db` è `healthy`.
+
+### Step 3: Configurare il Web Server
+
+```yaml
+ # Tier 1: Web - dipende dall'app
+ web:
+ image: nginx:alpine
+ container_name: lab03-web
+ hostname: web
+
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 1G
+
+ healthcheck:
+ test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
+ interval: 10s
+ timeout: 5s
+ retries: 3
+ start_period: 5s
+
+ ports:
+ - "127.0.0.1:8080:80"
+
+ # Dipende dall'app healthy
+ depends_on:
+ app:
+ condition: service_healthy
+
+ restart: unless-stopped
+```
+
+**Nota:** `web` parte SOLO quando `db` → `app` sono entrambi `healthy`.
+
+---
+
+## Parte 4: Avvio e Verifica
+
+### Step 1: Avviare tutti i servizi
+
+```bash
+cd labs/lab-03-compute
+docker compose up -d
+```
+
+### Step 2: Osservare l'ordine di avvio
+
+```bash
+# Monitora lo stato in tempo reale
+watch -n 2 'docker ps --format "table {{.Names}}\t{{.Status}}"'
+```
+
+**Sequenza prevista:**
+
+1. `lab03-db` parte → `starting` → `(healthy)` dopo ~10-20s
+2. `lab03-app` parte → `starting` → `(healthy)` dopo che `db` è healthy
+3. `lab03-web` parte → `starting` → `(healthy)` dopo che `app` è healthy
+
+### Step 3: Verificare l'ordine nei log
+
+```bash
+docker compose logs --tail=50
+```
+
+Cerca messaggi tipo:
+```
+lab03-db | database system is ready to accept connections
+lab03-app | Waiting for db to be healthy...
+lab03-web | Waiting for app to be healthy...
+```
+
+---
+
+## Parte 5: Testare le Dipendenze
+
+### Test 1: Riavviare il database
+
+```bash
+# Ferma il database
+docker compose stop db
+
+# Guarda lo stato di app e web
+docker ps
+```
+
+**Risultato:** `app` e `web` rimangono in esecuzione (non dipendono dal runtime di db, solo dall'avvio iniziale)
+
+### Test 2: Ferma e riavvia l'app
+
+```bash
+# Ferma l'app
+docker compose stop app
+
+# Guarda lo stato del web
+docker ps
+```
+
+**Risultato:** `web` continua a funzionare (stessa logica)
+
+### Test 3: Riavvio completo
+
+```bash
+# Ferma tutto
+docker compose down
+
+# Riavvia
+docker compose up -d
+
+# Osserva l'ordine di startup
+docker compose up -d && docker compose logs -f
+```
+
+Vedrai: `db` → `app` → `web` in ordine.
+
+---
+
+## Parte 6: Parallelismo AWS
+
+### ECS Task Definitions
+
+In AWS ECS, le dipendenze si configurano nel Task Definition:
+
+```json
+{
+ "containerDefinitions": [
+ {
+ "name": "db",
+ "essential": true,
+ "healthCheck": {
+ "command": ["CMD-SHELL", "pg_isready -U postgres"],
+ "interval": 10,
+ "timeout": 5,
+ "retries": 5
+ }
+ },
+ {
+ "name": "app",
+ "essential": true,
+ "dependsOn": [
+ {
+ "containerName": "db",
+ "condition": "HEALTHY"
+ }
+ ]
+ },
+ {
+ "name": "web",
+ "essential": true,
+ "dependsOn": [
+ {
+ "containerName": "app",
+ "condition": "HEALTHY"
+ }
+ ]
+ }
+ ]
+}
+```
+
+### Condizioni di Dipendenza ECS
+
+| Condizione | Docker Equivalente | Descrizione |
+|------------|-------------------|-------------|
+| START | `depends_on` (default) | Container avviato |
+| HEALTHY | `condition: service_healthy` | Healthcheck passing |
+| COMPLETE | - | Container exit code 0 |
+| SUCCESS | - | Container exit code 0 |
+
+---
+
+## Parte 7: Best Practices
+
+### 1. Usa Sempre service_healthy
+
+```yaml
+# ✓ GOOD
+depends_on:
+ db:
+ condition: service_healthy
+
+# ✗ AVOID (se possibile)
+depends_on:
+ - db
+```
+
+### 2. Configura healthcheck appropriati
+
+```yaml
+# Database - startup più lento
+healthcheck:
+ retries: 5
+ start_period: 10s
+
+# Web server - startup veloce
+healthcheck:
+ retries: 3
+ start_period: 5s
+```
+
+### 3. Previeni loop di dipendenze
+
+```yaml
+# ✗ WRONG - circular dependency
+web:
+ depends_on:
+ app: {condition: service_healthy}
+app:
+ depends_on:
+ web: {condition: service_healthy} # LOOP!
+
+# ✓ GOOD - directional dependency
+web:
+ depends_on:
+ app: {condition: service_healthy}
+app:
+ depends_on:
+ db: {condition: service_healthy} # OK
+db:
+ # No dependencies - base tier
+```
+
+---
+
+## Riepilogo
+
+In questo tutorial hai imparato:
+
+✓ **Concetto:** Dipendenze tra servizi per startup ordinato
+✓ **Sintassi:** `depends_on` con `condition: service_healthy`
+✓ **Pratica:** Architettura multi-tier Web → App → DB
+✓ **Verifica:** Osservare l'ordine di startup
+✓ **Parallelismo:** ECS `dependsOn` con condizione HEALTHY
+
+---
+
+## Complimenti!
+
+Hai completato i tutorial di Lab 03 - Compute & EC2!
+
+**Competenze Acquisite:**
+- Configurare limiti delle risorse (EC2 instance types)
+- Implementare healthchecks (ELB health checks)
+- Gestire dipendenze tra servizi (ECS task definitions)
+
+**Prossimi Passi:**
+- Esegui i test di verifica: `bash tests/run-all-tests.sh`
+- Esplora le guide How-to per procedure specifiche
+- Consulta i documenti Reference per sintassi completa
+- Leggi Explanation per approfondire i parallelismi cloud