PPhablo Vilas Boas

//

← All entries
July 06, 2026β€’21 min read

A Practical AWS Deployment Lab with Terraform, ECS Fargate, RDS, ALB, and TLS

A practical lab about deploying a Node.js service to AWS with Terraform, ECS Fargate, RDS PostgreSQL, Secrets Manager, an Application Load Balancer, ACM TLS, custom DNS, and cost validation.

Purpose

Some weeks ago, I developed a new backend with the plan of using it as a microservice to authenticate my applications. For this lab, I wanted to include a login process, and the best idea was to deploy this existing service. To do that, I needed to put the service into production, and I felt this was a very good opportunity to create a lab exploring the architecture and the IaC (Infrastructure as Code) behind it.

Before starting, the first thing was to analyze which resources this application uses. For that, I created this diagram:

                    +--------------+        +---------+----------+
                    | .env config  |        | auth210 API        |
                    | DB settings  |        | Node.js + Express  |
                    | JWT secret   | -----> | TypeScript         |
                    | API key      |        | Docker container   |
                    | OAuth creds  |        | Runtime port 3001  |
                    | SMTP creds   |        +----+-----------+---+
                    +--------------+             |           |
                                       pg client |           | nodemailer
                                                 v           v
                                  +--------------+--+     +--+----------------+
                                  | PostgreSQL 16   |     | SMTP mail service |
                                  | auth database   |     | password / OTP    |
                                  | persistent data |     | emails            |
                                  +--------------+--+     +-------------------+
                                                 |
                                                 | stores users, applications,
                                                 | tokens, OTPs and OAuth state
                                                 v
                                            +----+---------+
                                            | Data volume  |
                                            | pgdata       |
                                            +--------------+

With that map of the current application, the IaC structure became easier to define. The plan was to keep the infrastructure code close to the service, under infra/terraform, because the infrastructure depends directly on this API's Docker image, runtime variables, database connection, OAuth callback URLs, mail configuration, and secrets.

If you want to inspect the Terraform implementation in more detail, check the infra folder in the GitHub repository linked at the top of this post.

| Area              | Path                                 | Role                                                                                                                                                |
| ----------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Prod environment  | `infra/terraform/environments/prod`  | Entry point for the production apply, using `main.tf`, `variables.tf`, `outputs.tf`, `providers.tf`, `terraform.tfvars.example`, and `versions.tf`. |
| Networking module | `infra/terraform/modules/networking` | Creates the VPC, public and private subnets, internet gateway, route tables, security groups, and Application Load Balancer.                        |
| Database module   | `infra/terraform/modules/database`   | Creates RDS PostgreSQL, the database subnet group, the generated database password, and the database secret in Secrets Manager.                     |
| Service module    | `infra/terraform/modules/service`    | Creates ECR, ECS cluster, ECS Fargate service, task definition, container port `3001`, environment variables, secret injection, IAM task roles, ALB target group/listener, and CloudWatch log group. |

After applying the IaC, the public request path should be simple: internet traffic reaches the Application Load Balancer, the ALB forwards the request to the ECS Fargate task running the auth210 API, and the API connects to RDS PostgreSQL on port 5432 when it needs database access. The ECS task also reads secrets from Secrets Manager, writes logs to CloudWatch, pulls the image from ECR, and calls Google/GitHub OAuth plus SMTP providers.


Explaining the architecture


1. Use a single prod environment

I could have created a dev environment, but honestly this was unnecessary. This was a service with a lab purpose, so a single prod environment was enough.


2. Run the service with ECS Fargate

The next important decision was to run the service on ECS with Fargate instead of using the ECS EC2 launch type.

With ECS EC2 launch type, I would need to create and maintain the EC2 instances that run the containers. That gives more control over the machines, capacity, operating system, and some cost optimization strategies, especially when the workload is big enough to justify managing the cluster. The downside is that it adds more operational work: instance sizing, AMI updates, capacity planning, patching, autoscaling the EC2 layer, and thinking about the container hosts as another part of the infrastructure.

For this service, Fargate made more sense. I only need to define the task CPU, memory, image, environment variables, secrets, networking, and load balancer integration. AWS runs the container without me managing the underlying servers. The disadvantage is that Fargate gives less control over the host and can be more expensive than well-utilized EC2 capacity in larger workloads, but for this first version the simplicity is more valuable than that extra control.

So, below are some important points from my research:

| Topic                    | ECS Fargate                                                                                        | ECS EC2 launch type                                                           |
| ---------                | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| Best fit                 | Small services and teams that want to run containers without managing servers                      | Workloads that need more control over the container hosts                     |
| Setup speed              | Faster, because I only define the task, container, network, secrets, and load balancer integration | Slower, because I also need to define and maintain the EC2 capacity           |
| Infrastructure to manage | Lower, since AWS manages the underlying compute                                                    | Higher, since I need to manage the EC2 instances used by the cluster          |
| Host maintenance         | No direct host patching, AMI updates, or instance lifecycle work                                   | Requires patching, AMI updates, and instance lifecycle management             |
| Capacity planning        | Simpler, based mainly on task CPU, memory, and desired count                                       | More complex, because the EC2 cluster must have enough capacity for the tasks |
| Control                  | Less control over the underlying machine                                                           | More control over instance type, operating system, tuning, and placement      |
| Cost at small scale      | Good for this lab because it avoids extra operational complexity                                   | Can be overkill if the instance capacity is not well used                     |
| Cost at larger scale     | Can become more expensive than optimized EC2 capacity                                              | Can be cheaper when the EC2 instances are well utilized                       |
| Main disadvantage        | Less control and not always the cheapest option for long-running workloads at scale                | More infrastructure and operational responsibility                            |

The main advantage of Fargate is that it removes a whole layer of work from this lab. I do not need to think about the EC2 machines behind the cluster, patch them, resize them, or keep capacity available before the container can run. That makes the infrastructure easier to understand and easier to apply.

The main disadvantage is that I give up some control. With the EC2 launch type, I could choose the instances directly, tune the host layer, and maybe reduce costs later if the service became big enough to keep the machines well utilized. But that optimization is not useful yet.


3. Run only one ECS task

The next decision was to run only one task:

desired_count = 1

That means ECS keeps one copy of my API running. In a real production setup, running only one task is not ideal because a deployment, task crash, or Availability Zone issue can temporarily end availability. A more resilient setup would run at least two tasks, usually spread across different subnets and Availability Zones.

Even though the main idea for this auth service is to be generic enough to support different applications, the first consumers for now will only be my labs and the login flow for my portfolio. It does not have real traffic yet, and the purpose right now is to validate the infrastructure and runtime flow. Running two or more tasks would increase cost without giving me much practical value at this stage. So desired_count = 1 is intentional: cheaper, simpler, and enough for the current usage.


4. Use public ECS tasks restricted by security groups

The last decision was to keep the networking cheaper by not putting the ECS task in a fully private application path. The more production-like design would be:

+------------------+      HTTP request      +----------------------+
| Internet         | ---------------------> | Public ALB           |
| users / clients  |                        | port 80              |
+------------------+                        +----------+-----------+
                                                     |
                                                     | forwards traffic
                                                     v
                                          +----------+-----------+
                                          | Private ECS task    |
                                          | no public IP        |
                                          +----------+-----------+
                                                     |
                                                     | database connection
                                                     v
                                          +----------+-----------+
                                          | Private RDS         |
                                          | PostgreSQL          |
                                          +----------------------+

That is a better isolation model because the ECS task does not receive a public IP. However, the task still needs outbound access to pull images from ECR, read secrets from Secrets Manager, and send logs to CloudWatch. To do that from private subnets, I would usually need NAT Gateways or several VPC endpoints. Both options are valid, but they add more infrastructure and more cost.

For this lab, I selected public ECS tasks restricted by security groups:

+------------------+      HTTP request      +----------------------+
| Internet         | ---------------------> | Public ALB           |
| users / clients  |                        | port 80              |
+------------------+                        +----------+-----------+
                                                     |
                                                     | only ALB traffic
                                                     v
                                          +----------+-----------+
                                          | ECS task             |
                                          | public subnet        |
                                          +----------+-----------+
                                                     |
                                                     | database connection
                                                     v
                                          +----------+-----------+
                                          | Private RDS         |
                                          | PostgreSQL          |
                                          +----------------------+

The task can have the network access it needs without paying for NAT Gateways, and the security group still limits inbound traffic so the task only receives requests from the load balancer. It is not the most production-like style, but choosing private ECS tasks would require NAT Gateways or VPC Endpoints, which would add more cost and complexity to this simple lab. This is not the architecture I would blindly copy for every production system, but for this lab it was the best trade-off: lower cost, fewer moving parts, and still enough structure to learn the important AWS pieces.

In short, the first version optimizes for learning and cost control:

+-------------------------+        selected        +--------------------------+
| ECS Fargate             | ---------------------> | Less infrastructure     |
| instead of ECS EC2      |                        | management              |
+-------------------------+                        +--------------------------+

+-------------------------+        configured      +--------------------------+
| desired_count = 1       | ---------------------> | One running task        |
|                         |                        | because traffic is low  |
+-------------------------+                        +--------------------------+

+-------------------------+        selected        +--------------------------+
| Public ECS task         | ---------------------> | Avoid NAT Gateway /     |
| for the first version   |                        | VPC endpoint cost       |
+-------------------------+                        +--------------------------+

Making the API safe with TLS

Before exposing the auth service publicly, I needed to understand the right TLS shape for this architecture.

This backend handles login flows, OAuth callbacks, JWTs, cookies, and credentials. Sending that traffic over public HTTP would be unsafe because the data would cross the internet without TLS. Because this was an auth service, TLS was not something to add later as a nice improvement. It needed to be part of the first public version of the service.

The unsafe flow I wanted to avoid was:

+------------------+      HTTP :80       +----------------------+
| Browser / client | ------------------> | Application Load    |
|                  |                     | Balancer            |
+------------------+                     +----------+-----------+
                                                   |
                                                   | HTTP :3001
                                                   v
                                        +----------+-----------+
                                        | ECS Fargate task    |
                                        | auth-service        |
                                        +----------------------+

So the public entrypoint needed to be HTTPS from the start:

+------------------+      HTTPS :443     +----------------------+
| Browser / client | ------------------> | Application Load    |
|                  |                     | Balancer + ACM cert |
+------------------+                     +----------+-----------+
                                                   |
                                                   | HTTP :3001
                                                   v
                                        +----------+-----------+
                                        | ECS Fargate task    |
                                        | auth-service        |
                                        +----------------------+

The important detail is that TLS terminates at the Application Load Balancer. The ECS task can still receive HTTP on port 3001 because that part of the request stays behind the load balancer, inside the AWS network boundary. The public internet path is the part that must be HTTPS.

There were some DNS constraints too. My domain, phablovilasboas.tech, is managed in Hostinger, not in Route 53. Because of that, Terraform could not simply create all DNS records automatically inside AWS. I needed to keep DNS setup as a manual Hostinger step.

I also could not use the ALB IP address directly. An Application Load Balancer does not give me a fixed public IP; AWS can change the underlying addresses. The stable value AWS gives me is the ALB DNS name.

But using only the raw ALB DNS name was not enough either. It would look like an AWS-owned *.elb.amazonaws.com address, and I do not control that domain. I needed the public API to be available under my own domain so the certificate, browser validation, OAuth callback URLs, and frontend configuration all pointed to the same stable address.

There is also an important certificate detail here. I could not request an ACM certificate for the raw ALB DNS name, because I do not own elb.amazonaws.com. And even if the HTTPS listener had a certificate for my custom API domain, calling the ALB by its raw DNS name would not be the correct final URL because the browser would validate the hostname it is connecting to. The certificate, DNS name, OAuth callbacks, and frontend API URL all need to agree on the same domain.

My first domain idea was api.labs.phablovilasboas.tech, but in the middle of the setup I changed the final API domain to api.auth.phablovilasboas.tech. That became the canonical public URL for the service. From that point on, the ACM certificate, Hostinger CNAME, ALB HTTPS listener, OAuth callback URLs, frontend API URL, and final health check all needed to use api.auth.phablovilasboas.tech.

So the final DNS and TLS shape became:

+------------------------------+     CNAME      +----------------------+
| api.auth.phablovilasboas.tech| ------------> | AWS ALB DNS name     |
| Hostinger DNS                |               | *.elb.amazonaws.com  |
+------------------------------+               +----------+-----------+
                                                          |
                                                          | HTTPS listener :443
                                                          v
                                               +----------+-----------+
                                               | ACM certificate     |
                                               | same AWS region     |
                                               +----------+-----------+
                                                          |
                                                          | forwards HTTP :3001
                                                          v
                                               +----------+-----------+
                                               | ECS auth-service    |
                                               +----------------------+

The certificate should be an AWS Certificate Manager certificate for:

api.auth.phablovilasboas.tech

Because the ALB is in us-east-1, the ACM certificate also needs to be in us-east-1. Hostinger's SSL certificate is not the certificate used by the ALB. The ALB needs an ACM certificate that AWS can attach to the HTTPS listener.

The manual Hostinger records became:

<ACM validation name>  CNAME  <ACM validation value>
api.auth               CNAME  <ALB DNS name>

Then the ALB can keep port 80 open only to redirect requests to HTTPS, while port 443 becomes the real public entrypoint:

+------------------+      HTTP :80       +----------------------+
| Browser / client | ------------------> | ALB redirects to    |
|                  |                     | HTTPS :443          |
+------------------+                     +----------------------+

+------------------+      HTTPS :443     +----------------------+
| Browser / client | ------------------> | ALB forwards to ECS |
|                  |                     | target group        |
+------------------+                     +----------------------+

This solved the main problems:

  • public auth traffic is no longer HTTP-only;
  • the API has a stable domain controlled by me;
  • Hostinger remains the DNS provider;
  • ACM handles the certificate used by the ALB;
  • HTTP requests can redirect to HTTPS instead of silently allowing unsafe access.

Applying the real image in AWS

After the infrastructure shape was already defined, the next step was to deploy a real image instead of keeping the Terraform plan as only a theoretical architecture.

The image follows the tag convention based on the current Git commit:

AWS_REGION=us-east-1
ACCOUNT_ID=<aws-account-id>
TAG=git-$(git rev-parse --short HEAD)
REPOSITORY_URL="$ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/auth-service-prod"
IMAGE_URI="$REPOSITORY_URL:$TAG"

echo "$IMAGE_URI"

The image selected for this deploy was:

<aws-account-id>.dkr.ecr.us-east-1.amazonaws.com/auth-service-prod:git-bab4fe1

That value is important because it is the image URI passed to Terraform through container_image. Terraform does not build the application image. Terraform only creates the AWS resources and configures the ECS task definition to use that image tag.

In this deploy, the first apply was useful because it created the AWS side: ECR repository, ECS cluster, ECS service, ALB, target group, RDS, Secrets Manager, CloudWatch, and the budget guardrail. After that, the application image and the runtime secrets still needed to be present before ECS could keep the task running.

Then I executed the production plan:

terraform -chdir=infra/terraform/environments/prod plan

And applied it:

terraform -chdir=infra/terraform/environments/prod apply

The apply created the first real version of the AWS architecture:

Apply complete! Resources: 41 added, 0 changed, 0 destroyed.

The most important outputs from the first apply were:

alb_dns_name = "auth-service-prod-alb-<alb-id>.us-east-1.elb.amazonaws.com"
alb_url = "https://api.labs.phablovilasboas.tech"
api_domain_name = "api.labs.phablovilasboas.tech"
api_url = "https://api.labs.phablovilasboas.tech"
budget_name = "auth-service-prod-monthly-budget"
cloudwatch_log_group_name = "/ecs/auth-service-prod"
ecr_repository_url = "<aws-account-id>.dkr.ecr.us-east-1.amazonaws.com/auth-service-prod"
ecs_cluster_name = "auth-service-prod-cluster"
ecs_service_name = "auth-service-prod-service"
rds_endpoint = "auth-service-prod-postgres.<rds-id>.us-east-1.rds.amazonaws.com:5432"
rds_secret_arn = "arn:aws:secretsmanager:us-east-1:<aws-account-id>:secret:auth-service-prod/db-password-<secret-suffix>"

This output is a good checkpoint because it proves Terraform now has real AWS resources to connect. At this point, the domain output still reflected my first domain choice, api.labs.phablovilasboas.tech. Later, after the DNS and ACM validation path changed, the final public API domain became api.auth.phablovilasboas.tech.

  • the first public entrypoint configured in Terraform was the custom domain api.labs.phablovilasboas.tech;
  • the final public API domain became api.auth.phablovilasboas.tech;
  • the DNS target for Hostinger is the ALB DNS name auth-service-prod-alb-<alb-id>.us-east-1.elb.amazonaws.com;
  • the deployed container image comes from the auth-service-prod ECR repository;
  • ECS created the cluster auth-service-prod-cluster and service auth-service-prod-service;
  • RDS created the PostgreSQL endpoint, but it is still private and should only be reached from inside the VPC path allowed by security groups;
  • Secrets Manager stores the generated database password and the application secrets used by the ECS task;
  • CloudWatch receives logs from the running task;
  • AWS Budgets adds a cost guardrail for the lab.

The final architecture after the DNS and certificate correction is:

Internet
  -> api.auth.phablovilasboas.tech
  -> Hostinger CNAME
  -> auth-service-prod ALB
  -> HTTPS listener :443
  -> target group with health check /health
  -> ECS Fargate service auth-service-prod-service
  -> Docker image git-bab4fe1 from ECR
  -> private RDS PostgreSQL
  -> Secrets Manager
  -> CloudWatch Logs

At this point, I pushed the real image to ECR:

AWS_REGION=us-east-1
ACCOUNT_ID=<aws-account-id>
IMAGE_URI="<aws-account-id>.dkr.ecr.us-east-1.amazonaws.com/auth-service-prod:git-bab4fe1"

aws ecr get-login-password --region "$AWS_REGION" \
  | docker login --username AWS --password-stdin "$ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com"

docker build -t "$IMAGE_URI" .
docker push "$IMAGE_URI"

The pushed image finished with this digest:

git-bab4fe1: digest: sha256:<image-digest> size: 856

Then I populated the application secrets that Terraform had created as placeholders:

aws secretsmanager put-secret-value --region us-east-1 --secret-id "auth-service-prod/jwt-secret" --secret-string "..."
aws secretsmanager put-secret-value --region us-east-1 --secret-id "auth-service-prod/google-oauth-client-id" --secret-string "..."
aws secretsmanager put-secret-value --region us-east-1 --secret-id "auth-service-prod/google-oauth-client-secret" --secret-string "..."
aws secretsmanager put-secret-value --region us-east-1 --secret-id "auth-service-prod/github-oauth-client-id" --secret-string "..."
aws secretsmanager put-secret-value --region us-east-1 --secret-id "auth-service-prod/github-oauth-client-secret" --secret-string "..."
aws secretsmanager put-secret-value --region us-east-1 --secret-id "auth-service-prod/mail-user" --secret-string "..."
aws secretsmanager put-secret-value --region us-east-1 --secret-id "auth-service-prod/mail-password" --secret-string "..."

After applying Terraform again, I checked the ECS service:

aws ecs describe-services \
  --region us-east-1 \
  --cluster auth-service-prod-cluster \
  --services auth-service-prod-service \
  --query 'services[0].{desired:desiredCount,running:runningCount,pending:pendingCount,status:status}'

The first result showed that the service existed, but the task was not running:

{
  "desired": 1,
  "running": 0,
  "pending": 0,
  "status": "ACTIVE"
}

That was a useful failure because it confirmed the service itself was active, but the task could not stay alive. The missing piece was the application API key secret. I had populated the OAuth, mail, and JWT values, but forgot:

auth-service-prod/api-key

After adding it:

aws secretsmanager put-secret-value \
  --region us-east-1 \
  --secret-id "auth-service-prod/api-key" \
  --secret-string "..."

The same ECS check returned the expected state:

{
  "desired": 1,
  "running": 1,
  "pending": 0,
  "status": "ACTIVE"
}

I also used the raw ALB DNS as an infrastructure checkpoint earlier in the process:

curl http://auth-service-prod-alb-<alb-id>.us-east-1.elb.amazonaws.com/health

When it returned the API response, it proved that the ALB could reach the ECS task:

{"status":"ok"}

This was not the final public URL that users and frontend applications should use. The final URL became https://api.auth.phablovilasboas.tech, but testing the raw ALB DNS was still useful because it proved the path from ALB -> target group -> ECS task -> container /health route was working.


Testing the deployed services

After applying Terraform, I felt it was necessary to do some validation to make sure everything had been done correctly.


1. Validate the ECR image

The ECS task definition depends on this image:

<aws-account-id>.dkr.ecr.us-east-1.amazonaws.com/auth-service-prod:git-bab4fe1

In AWS, I opened ECR, selected the auth-service-prod repository, and confirmed that the git-bab4fe1 image tag existed.


2. Validate the ECS service

In ECS, I opened the cluster auth-service-prod-cluster and then opened the service auth-service-prod-service. It had only one task, which was the expected behavior.


3. Validate the task definition

I looked at the task definition for auth-service-prod-service, and everything appeared correct:


4. Validate the Application Load Balancer and listeners

This is the public entrypoint. In AWS, I opened EC2 > Load Balancers and selected auth-service-prod-alb. The expected listener setup is:

  • port 80 redirects to HTTPS 443;
  • port 443 forwards to the ECS target group;
  • the HTTPS listener uses the ACM certificate for api.auth.phablovilasboas.tech.


5. Validate ACM and DNS

I went to ACM and checked the certificate validation. It looked good too:


6. Validate RDS

In AWS, I opened RDS > Databases and selected auth-service-prod-postgres:

The endpoint from Terraform was:

auth-service-prod-postgres.<rds-id>.us-east-1.rds.amazonaws.com:5432

The important validations are:

  • status is Available;


7. Validate Secrets Manager

This should contain all of the environment secrets used by the ECS task:


8. Test the health endpoint

Finally, everything looked okay, so the last test was to check if the API was running. I used the /health route as the final infrastructure test.

First, I checked the raw ALB DNS. The screenshot shows a 301 redirect:

curl http://auth-service-prod-alb-<alb-id>.us-east-1.elb.amazonaws.com/health

This redirect comes from the ALB HTTP listener rule. ACM provides the certificate used by the HTTPS listener, but the redirect behavior itself belongs to the load balancer listener configuration.

Then, testing the final DNS, I got:

curl https://api.auth.phablovilasboas.tech/health

The expected response was:


Cost Analysis

After leaving the service running and checking the AWS Billing dashboard, the cost looked reasonable for this lab.

The dashboard showed a Month-to-date cost of $3.43. AWS also estimated a Total forecasted cost for current month of $17.36. This is an important number because it gives a practical baseline for this architecture: one ECS Fargate task, one RDS PostgreSQL database, an Application Load Balancer, CloudWatch logs, ECR, Secrets Manager, and the supporting network resources.

Using a rough conversion close to R$5.50 per dollar, the forecasted monthly cost would be:

$17.36 * R$5.50 = R$95.48

So, in practice, I would round this to something around R$100/month. For a real production service this would need more analysis, but for a small lab and portfolio auth service, around R$100/month is acceptable.

There is one detail from the screenshot that is worth keeping in mind:

  • the previous month is $0.00, so the huge percentage increase shown by AWS is expected because this infrastructure did not exist before;

Based on this first billing check, the architecture decisions made earlier helped keep the cost under control. Running only one ECS task and avoiding NAT Gateways were especially important choices. If I had used private ECS tasks with NAT Gateways from the beginning, the monthly cost would probably be higher than this first $17.36 forecast.


Future Improvements

  • Evaluate ECS EC2 launch type if the service starts to have steady traffic and the container capacity becomes large enough to justify managing EC2 instances directly. For now, Fargate is still the simpler and better choice for this lab.
  • Move the ECS tasks to private subnets. In this first version, the tasks remained in public subnets to avoid the extra cost of NAT Gateways or VPC Endpoints, but a more production-like architecture would keep the ALB public and the tasks private.
  • Run more than one ECS task. desired_count = 1 is enough for this lab, but it is not ideal for availability. A better production setup would run at least two tasks across different Availability Zones.
  • Add a cache strategy with ElastiCache when there is a real performance reason for it. This could help reduce repeated reads from RDS PostgreSQL, but it should be used carefully in an auth service because cached user, token, or permission data needs clear expiration and invalidation rules.

Conclusion

This lab started with a simple goal: take an existing Node.js auth service and make it run as a real AWS service using Terraform. The final result was more than just a running container. The service now has an ECR image, an ECS Fargate deployment, an RDS PostgreSQL database, Secrets Manager configuration, CloudWatch logs, an Application Load Balancer, a custom domain, and HTTPS through ACM.

The most important part was understanding the tradeoffs. I chose Fargate because it reduced infrastructure management, one ECS task because the current traffic is small, public ECS tasks because this is a cost-conscious lab, and TLS from the beginning because this is an auth service. The final health check through https://api.auth.phablovilasboas.tech/health confirmed that the public path was working end to end.

For this lab, this version achieved the main objective: the auth service is no longer just local code. It is deployed, reachable through HTTPS, and connected to the AWS infrastructure it needs to run.

ABOUT THE AUTHOR

Phablo Vilas Boas β€” Tech Lead & Senior Full Stack Engineer with 9+ years building platforms with Node.js, Python, React, and Flutter.

Continue reading