{"id":370,"date":"2026-01-21T09:37:23","date_gmt":"2026-01-21T09:37:23","guid":{"rendered":"https:\/\/infivit.com\/blog\/?p=370"},"modified":"2026-01-21T09:37:26","modified_gmt":"2026-01-21T09:37:26","slug":"localstack","status":"publish","type":"post","link":"https:\/\/infivit.com\/blog\/localstack\/","title":{"rendered":"Reducing Client AWS Costs by 70% with LocalStack"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1024\" height=\"534\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-14-1024x534.png\" alt=\"\" class=\"wp-image-378\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-14-1024x534.png 1024w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-14-300x156.png 300w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-14-768x401.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-14.png 1388w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>As a cloud consulting company, we&#8217;ve seen our share of AWS bills. But when our client forwarded us their monthly statement with a concerned email, even we were surprised.<\/p>\n\n\n\n<p><strong>$3,247 for the month.<\/strong><\/p>\n\n\n\n<p>The breakdown was what really caught our attention:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Production:<\/strong> $1,400<\/li>\n\n\n\n<li><strong>Development &amp; Staging:<\/strong> $1,847<\/li>\n<\/ul>\n\n\n\n<p>They were literally spending more on building the product than running it in production.<\/p>\n\n\n\n<p>The client, a fast-growing SaaS company, had approached us to optimize their AWS costs. We expected to find inefficiencies in production environments\u2014oversized instances, unused resources, that sort of thing. Instead, we discovered something completely different.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Development was the real problem<\/h2>\n\n\n\n<p>Six months earlier, the team had just 3 developers, a serverless AWS architecture, and manageable cloud bills. Nothing unusual. Then the team grew to 8 developers, and things got expensive fast.<\/p>\n\n\n\n<p>After a junior developer accidentally deleted a shared AWS resource (we&#8217;ve all been there), the team made what seemed like a reasonable decision: <strong>&#8220;Each developer should have their own AWS environment.&#8221;<\/strong><\/p>\n\n\n\n<p>In practice, this meant:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>8 developers meant 8 full AWS stacks running 24\/7.<\/li>\n\n\n\n<li>Duplicated Lambda functions, DynamoDB tables, S3 buckets across all environments.<\/li>\n\n\n\n<li><strong>$780 per month<\/strong> just for developer environments.<\/li>\n<\/ul>\n\n\n\n<p>To their credit, they followed AWS best practices almost perfectly. Integration tests on every pull request, temporary AWS environments spun up per PR, tests running against real AWS services.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Their monthly CI\/CD costs:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>ECS Fargate containers:<\/strong> $300<\/li>\n\n\n\n<li><strong>RDS test databases:<\/strong> $60<\/li>\n\n\n\n<li><strong>CloudFormation operations:<\/strong> $80<\/li>\n\n\n\n<li><strong>Data transfer:<\/strong> $42<\/li>\n\n\n\n<li><strong>Miscellaneous charges:<\/strong> $42<\/li>\n\n\n\n<li><strong>Total:<\/strong> $524 per month.<\/li>\n<\/ul>\n\n\n\n<p>Like most growing teams, they also had the usual suspects: EC2 instances left running after tests, an Elasticsearch domain from &#8220;that one experiment&#8221; three months ago, orphaned load balancers nobody remembered creating. Quietly adding another <strong>$145 per month<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The productivity hit was worse than the costs<\/h2>\n\n\n\n<p>We shadowed the team for a day to understand their workflow. Here&#8217;s what a typical bug-fix cycle looked like:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Write code (<strong>3 minutes<\/strong>)<\/li>\n\n\n\n<li>Deploy to AWS (<strong>3 minutes<\/strong>)<\/li>\n\n\n\n<li>Wait for deployment (<strong>5+ minutes<\/strong>)<\/li>\n\n\n\n<li>Test (<strong>2 minute<\/strong>)<\/li>\n\n\n\n<li>Check CloudWatch logs (<strong>1 minute<\/strong>)<\/li>\n\n\n\n<li>Find another issue<\/li>\n\n\n\n<li><strong>Repeat 5-8 times per bug<\/strong><\/li>\n<\/ol>\n\n\n\n<p>Developers spent roughly <strong>60% of their time just waiting<\/strong>. The irony wasn&#8217;t lost on us. The cloud was meant to accelerate development, but it was actually slowing them down.<\/p>\n\n\n\n<p>Every meaningful change required a pull request. Every pull request required review. Every review took time. While waiting, AWS resources kept running, test executions kept consuming credits, and repeated experiments became expensive.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">As experimentation increased, so did the friction:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Spinning up AWS resources for every test iteration.<\/li>\n\n\n\n<li>Paying for services even when nothing productive was happening.<\/li>\n\n\n\n<li>Slow feedback loops while waiting for cloud deployments.<\/li>\n\n\n\n<li>Risk of breaking shared dev environments.<\/li>\n\n\n\n<li>Reluctance to experiment freely because cleanup was never trivial.<\/li>\n<\/ul>\n\n\n\n<p>There was no local debugging. Period. So developers relied on <code>console.log()<\/code> driven development. Deploy, wait, check logs, repeat. 30-60 second delays before logs appeared in CloudWatch. No breakpoints, no variable inspection, no stepping through code.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\"I spend more time waiting for deployments than actually coding.\" \u2014 <em>Senior Engineer<\/em><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Enter LocalStack<\/h2>\n\n\n\n<p>While reviewing their infrastructure, we evaluated several local AWS emulation options and landed on <strong>LocalStack<\/strong>. It sounded too good to be true: a complete AWS-like environment running locally.<\/p>\n\n\n\n<p>LocalStack is a local cloud emulator that runs in a Docker container on your machine. From the application&#8217;s point of view, nothing changes: Same AWS SDKs, same CLI commands, same APIs, and same infrastructure patterns. <strong>The only difference is the endpoint URL.<\/strong><\/p>\n\n\n\n<p>From here, you have options on how to get started, diving deeper into using LocalStack, and so on.<\/p>\n\n\n\n<p>Select the&nbsp;<strong>My License<\/strong>&nbsp;option on the sidebar. It should reveal the following:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"838\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-31-1024x838.png\" alt=\"\" class=\"wp-image-396\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-31-1024x838.png 1024w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-31-300x245.png 300w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-31-768x628.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-31-1536x1257.png 1536w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-31.png 1816w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p id=\"0465\">For this demo, we will stick with the free-trial version, but feel free to select and read about others if you like.<\/p>\n\n\n\n<p id=\"d865\">After selecting the free-trial option, you will need to fill out the remaining personal information to proceed. Once all that is complete, navigate back to the dashboard and select the&nbsp;<strong>Auth Tokens&nbsp;<\/strong>selection on the sidebar.<\/p>\n\n\n\n<p id=\"031f\">It should reveal the following:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"527\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-22-1024x527.png\" alt=\"\" class=\"wp-image-387\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-22-1024x527.png 1024w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-22-300x154.png 300w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-22-768x395.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-22-1536x790.png 1536w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-22-2048x1054.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p id=\"007c\">This page generates a personal authentication token for you. You also have the option of resetting it anytime for security reasons.<\/p>\n\n\n\n<p id=\"4a0e\">You will need to add this as an environment variable locally in order to work with the LocalStack API.<\/p>\n\n\n\n<p id=\"f097\">Head over to the terminal and paste in the command as is:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"167\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-23-1024x167.png\" alt=\"\" class=\"wp-image-388\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-23-1024x167.png 1024w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-23-300x49.png 300w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-23-768x125.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-23-1536x250.png 1536w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-23.png 1754w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p id=\"f097\">Whenever we make calls using the CLI, we are in fact, making an API call to LocalStack behind the scenes and we will be authenticated using this token.<\/p>\n\n\n\n<p id=\"dfc3\">Your freedom of calls depends on your subscription (free-trial\/paid). For more advanced features, you will need a paid subscription.<\/p>\n\n\n\n<p id=\"f097\">This concludes setting up the LocalStack account. Now, it is on with local installation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why This Changed Everything<\/h3>\n\n\n\n<p>Once we integrated LocalStack into our workflow, the impact was immediate and measurable.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Task processing became nearly <strong>60% faster<\/strong><\/li>\n\n\n\n<li>Infrastructure spin-up time dropped to <strong>seconds<\/strong><\/li>\n\n\n\n<li>AWS costs during development were almost <strong>eliminated<\/strong><\/li>\n\n\n\n<li>Developers experimented more freely<\/li>\n\n\n\n<li>PR review cycles shortened because issues were caught earlier<\/li>\n<\/ul>\n\n\n\n<p>Most importantly, feedback became instant.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"370\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-15-1024x370.png\" alt=\"\" class=\"wp-image-379\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-15-1024x370.png 1024w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-15-300x108.png 300w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-15-768x277.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-15.png 1362w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">How it actually works<\/h3>\n\n\n\n<p>LocalStack runs as a single Docker container exposing AWS-style endpoints, typically on <code>http:\/\/localhost:4566<\/code>.<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>The request hits LocalStack instead of AWS.<\/li>\n\n\n\n<li>LocalStack routes it to the correct service emulator.<\/li>\n\n\n\n<li>The emulator processes the request.<\/li>\n\n\n\n<li>A response identical to AWS is returned.<\/li>\n<\/ol>\n\n\n\n<p>State is preserved. If you create an S3 bucket, it exists until you delete it. You can even configure persistence so the state survives container restarts.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The networking piece<\/h3>\n\n\n\n<p>LocalStack emulates AWS networking components (VPCs, subnets, route tables) at the API and state level. This allows developers to validate networking architecture and IAM policies without provisioning real VPC networking.<\/p>\n\n\n\n<p>Example Terraform validation:<\/p>\n\n\n\n<p>Terraform<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>resource \"aws_vpc\" \"main\" {\n  cidr_block = \"10.0.0.0\/16\"\n  tags = {\n    Name = \"local-vpc\"\n  }\n}\n\nresource \"aws_subnet\" \"public\" {\n  vpc_id     = aws_vpc.main.id\n  cidr_block = \"10.0.1.0\/24\"\n  tags = {\n    Name = \"public-subnet\"\n  }\n}\n\nresource \"aws_security_group\" \"web_sg\" {\n  name   = \"web-sg\"\n  vpc_id = aws_vpc.main.id\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = &#91;\"0.0.0.0\/0\"]\n  }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">What LocalStack can&#8217;t do<\/h2>\n\n\n\n<p>Networking constructs are simulated rather than enforced at the packet level. It is <strong>not<\/strong> suitable for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Performance benchmarking.<\/li>\n\n\n\n<li>Latency-sensitive networking tests.<\/li>\n\n\n\n<li>Validating real traffic flow between isolated subnets.<\/li>\n\n\n\n<li>Load testing at scale.<\/li>\n<\/ul>\n\n\n\n<p>In practice, LocalStack excels at <strong>IaC validation, CI\/CD testing, IAM policy verification, and catching configuration errors early.<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Building a Serverless App with LocalStack ( Demo)<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Installing LocalStack<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>pip install localstack\npip install awscli-local<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Starting LocalStack<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>localstack start -d\nlocalstack status<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"235\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-16-1024x235.png\" alt=\"\" class=\"wp-image-380\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-16-1024x235.png 1024w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-16-300x69.png 300w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-16-768x176.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-16.png 1386w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"542\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-24-1024x542.png\" alt=\"\" class=\"wp-image-389\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-24-1024x542.png 1024w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-24-300x159.png 300w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-24-768x407.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-24-1536x814.png 1536w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-24-2048x1085.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Testing LocalStack<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Set up environment variables\nexport AWS_ACCESS_KEY_ID=test\nexport AWS_SECRET_ACCESS_KEY=test\nexport AWS_DEFAULT_REGION=us-east-1\n\n# Create and list an S3 bucket\nawslocal s3 mb s3:\/\/my-test-bucket\nawslocal s3 ls<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"219\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-17-1024x219.png\" alt=\"\" class=\"wp-image-381\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-17-1024x219.png 1024w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-17-300x64.png 300w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-17-768x165.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-17.png 1204w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"220\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-25-1024x220.png\" alt=\"\" class=\"wp-image-390\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-25-1024x220.png 1024w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-25-300x65.png 300w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-25-768x165.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-25-1536x330.png 1536w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-25-2048x441.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: ECS on LocalStack Demo<\/h3>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"878\" height=\"1024\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-26-878x1024.png\" alt=\"\" class=\"wp-image-391\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-26-878x1024.png 878w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-26-257x300.png 257w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-26-768x896.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-26-1317x1536.png 1317w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-26-1756x2048.png 1756w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-26.png 1910w\" sizes=\"(max-width: 878px) 100vw, 878px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>terraform {\n  required_providers {\n    aws = {\n      source  = \"hashicorp\/aws\"\n      version = \"~&gt; 5.0\"\n    }\n  }\n}\n\nprovider \"aws\" {\n  access_key                  = \"test\"\n  secret_key                  = \"test\"\n  region                      = \"us-east-1\"\n  skip_credentials_validation = true\n  skip_metadata_api_check     = true\n  skip_requesting_account_id  = true\n\n  endpoints {\n    ec2 = \"http:\/\/localhost:4566\"\n    ecs = \"http:\/\/localhost:4566\"\n    ecr = \"http:\/\/localhost:4566\"\n    iam = \"http:\/\/localhost:4566\"\n    logs = \"http:\/\/localhost:4566\"\n  }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Create main.tf<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># ECS Cluster\nresource \"aws_ecs_cluster\" \"demo_cluster\" {\n  name = \"demo-cluster\"\n\n  tags = {\n    Environment = \"demo\"\n    ManagedBy   = \"terraform\"\n  }\n}\n\n# IAM Role for ECS Task Execution\nresource \"aws_iam_role\" \"ecs_task_execution_role\" {\n  name = \"ecs-task-execution-role\"\n\n  assume_role_policy = jsonencode({\n    Version = \"2012-10-17\"\n    Statement = &#91;\n      {\n        Action = \"sts:AssumeRole\"\n        Effect = \"Allow\"\n        Principal = {\n          Service = \"ecs-tasks.amazonaws.com\"\n        }\n      }\n    ]\n  })\n}\n\n# IAM Role for ECS Task\nresource \"aws_iam_role\" \"ecs_task_role\" {\n  name = \"ecs-task-role\"\n\n  assume_role_policy = jsonencode({\n    Version = \"2012-10-17\"\n    Statement = &#91;\n      {\n        Action = \"sts:AssumeRole\"\n        Effect = \"Allow\"\n        Principal = {\n          Service = \"ecs-tasks.amazonaws.com\"\n        }\n      }\n    ]\n  })\n}\n\n# CloudWatch Log Group\nresource \"aws_cloudwatch_log_group\" \"ecs_logs\" {\n  name              = \"\/ecs\/demo-app\"\n  retention_in_days = 7\n}\n\n# ECS Task Definition\nresource \"aws_ecs_task_definition\" \"demo_task\" {\n  family                   = \"demo-task\"\n  network_mode             = \"awsvpc\"\n  requires_compatibilities = &#91;\"FARGATE\"]\n  cpu                      = \"256\"\n  memory                   = \"512\"\n  execution_role_arn       = aws_iam_role.ecs_task_execution_role.arn\n  task_role_arn            = aws_iam_role.ecs_task_role.arn\n\n  container_definitions = jsonencode(&#91;\n    {\n      name      = \"demo-container\"\n      image     = \"nginx:latest\"\n      essential = true\n\n      portMappings = &#91;\n        {\n          containerPort = 80\n          hostPort      = 80\n          protocol      = \"tcp\"\n        }\n      ]\n\n      logConfiguration = {\n        logDriver = \"awslogs\"\n        options = {\n          \"awslogs-group\"         = aws_cloudwatch_log_group.ecs_logs.name\n          \"awslogs-region\"        = \"us-east-1\"\n          \"awslogs-stream-prefix\" = \"demo\"\n        }\n      }\n\n      environment = &#91;\n        {\n          name  = \"ENV\"\n          value = \"demo\"\n        }\n      ]\n    }\n  ])\n}\n\n# Default VPC (LocalStack creates this automatically)\ndata \"aws_vpc\" \"default\" {\n  default = true\n}\n\ndata \"aws_subnets\" \"default\" {\n  filter {\n    name   = \"vpc-id\"\n    values = &#91;data.aws_vpc.default.id]\n  }\n}\n\n# Security Group\nresource \"aws_security_group\" \"ecs_tasks\" {\n  name        = \"ecs-tasks-sg\"\n  description = \"Allow inbound traffic for ECS tasks\"\n  vpc_id      = data.aws_vpc.default.id\n\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = &#91;\"0.0.0.0\/0\"]\n  }\n\n  egress {\n    from_port   = 0\n    to_port     = 0\n    protocol    = \"-1\"\n    cidr_blocks = &#91;\"0.0.0.0\/0\"]\n  }\n\n  tags = {\n    Name = \"ecs-tasks-sg\"\n  }\n}\n\n# ECS Service\nresource \"aws_ecs_service\" \"demo_service\" {\n  name            = \"demo-service\"\n  cluster         = aws_ecs_cluster.demo_cluster.id\n  task_definition = aws_ecs_task_definition.demo_task.arn\n  desired_count   = 1\n  launch_type     = \"FARGATE\"\n\n  network_configuration {\n    subnets          = data.aws_subnets.default.ids\n    security_groups  = &#91;aws_security_group.ecs_tasks.id]\n    assign_public_ip = true\n  }\n\n  tags = {\n    Environment = \"demo\"\n  }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3: Create outputs.tf<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>output \"cluster_name\" {\n  description = \"Name of the ECS cluster\"\n  value       = aws_ecs_cluster.demo_cluster.name\n}\n\noutput \"cluster_arn\" {\n  description = \"ARN of the ECS cluster\"\n  value       = aws_ecs_cluster.demo_cluster.arn\n}\n\noutput \"service_name\" {\n  description = \"Name of the ECS service\"\n  value       = aws_ecs_service.demo_service.name\n}\n\noutput \"task_definition_arn\" {\n  description = \"ARN of the task definition\"\n  value       = aws_ecs_task_definition.demo_task.arn\n}\n\noutput \"security_group_id\" {\n  description = \"ID of the security group\"\n  value       = aws_security_group.ecs_tasks.id\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Step 4: Deploy and Test<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>terraform init\nterraform plan\nterraform apply -auto-approve<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"430\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-20-1024x430.png\" alt=\"\" class=\"wp-image-384\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-20-1024x430.png 1024w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-20-300x126.png 300w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-20-768x323.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-20.png 1252w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"626\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-27-1024x626.png\" alt=\"\" class=\"wp-image-392\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-27-1024x626.png 1024w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-27-300x183.png 300w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-27-768x470.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-27-1536x939.png 1536w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-27-2048x1252.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"239\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-28-1024x239.png\" alt=\"\" class=\"wp-image-393\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-28-1024x239.png 1024w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-28-300x70.png 300w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-28-768x179.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-28-1536x358.png 1536w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-28-2048x478.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"231\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-29-1024x231.png\" alt=\"\" class=\"wp-image-394\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-29-1024x231.png 1024w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-29-300x68.png 300w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-29-768x173.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-29-1536x347.png 1536w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-29-2048x462.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"255\" src=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-30-1024x255.png\" alt=\"\" class=\"wp-image-395\" srcset=\"https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-30-1024x255.png 1024w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-30-300x75.png 300w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-30-768x191.png 768w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-30-1536x382.png 1536w, https:\/\/infivit.com\/blog\/wp-content\/uploads\/image-30-2048x510.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Three Months Later: The Numbers<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Cost reduction: 70%<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Before:<\/strong> $1,847 per month<\/li>\n\n\n\n<li><strong>After:<\/strong> $550 per month (LocalStack Base licenses + minimal shared AWS staging)<\/li>\n\n\n\n<li><strong>Annual savings:<\/strong> $15,600<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Development speed: 5\u00d7 faster feedback<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Bug fixes:<\/strong> 62% faster<\/li>\n\n\n\n<li><strong>Feature delivery:<\/strong> 33% faster<\/li>\n\n\n\n<li><strong>Deploy\/test cycle:<\/strong> 54\u00d7 faster<\/li>\n\n\n\n<li><strong>Features shipped per sprint:<\/strong> up 50%<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Quality improvements<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Bugs caught before production:<\/strong> jumped from 77% to 94%<\/li>\n\n\n\n<li><strong>Production incidents:<\/strong> down 40%<\/li>\n\n\n\n<li><strong>Bug-related support tickets:<\/strong> down 35%<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Time saved<\/h3>\n\n\n\n<p>Roughly <strong>300 developer hours saved per month<\/strong>, or about <strong>$18,000 per month<\/strong> in reclaimed productivity.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Challenges and Solutions<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Service parity?<\/strong> LocalStack Pro and a minimal staging environment for edge cases.<\/li>\n\n\n\n<li><strong>High memory?<\/strong> Enable only needed services (e.g., <code>export SERVICES=s3,lambda<\/code>). Allocate 4GB RAM to Docker.<\/li>\n\n\n\n<li><strong>Accidental AWS calls?<\/strong> Use <code>.env.local<\/code> defaults.<\/li>\n\n\n\n<li><strong>Resources not found?<\/strong> Ensure <code>export AWS_DEFAULT_REGION=us-east-1<\/code> is set.<\/li>\n\n\n\n<li><strong>Won&#8217;t start?<\/strong> Run <code>docker ps<\/code> to check port 4566.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Essential Commands &amp; Best Practices<\/h2>\n\n\n\n<p><strong>Daily Management:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>localstack start -d<\/code> \/ <code>localstack stop<\/code><\/li>\n\n\n\n<li><code>localstack logs<\/code><\/li>\n\n\n\n<li><code>awslocal s3 ls<\/code> \/ <code>awslocal lambda list-functions<\/code><\/li>\n<\/ul>\n\n\n\n<p><strong>Best Practices:<\/strong><\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Multi-environment code:<\/strong> Use dynamic blocks in Terraform to toggle LocalStack endpoints.<\/li>\n\n\n\n<li><strong>Persistence:<\/strong> Always use <code>PERSISTENCE=1<\/code> and volumes.<\/li>\n\n\n\n<li><strong>Staging environment:<\/strong> Keep a real AWS staging for final integration checks.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">The Verdict: When this makes sense<\/h2>\n\n\n\n<p><strong>Ideal for:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>3+ developers on AWS infra.<\/li>\n\n\n\n<li>Serverless or microservices architecture.<\/li>\n\n\n\n<li>AWS dev costs over $500\/month.<\/li>\n\n\n\n<li>Integration tests in CI\/CD.<\/li>\n<\/ul>\n\n\n\n<p><strong>Not ideal for:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Very small teams (1-2 devs).<\/li>\n\n\n\n<li>Highly specialized AWS workloads not yet supported.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Looking Back<\/h2>\n\n\n\n<p>We went looking for cost optimizations. What we found instead was a broken development workflow. LocalStack didn&#8217;t just reduce costs\u2014it fundamentally changed how the team worked.<\/p>\n\n\n\n<p><strong>Start today. Install LocalStack. Build something. Break it. Fix it. Learn. All without spending a cent on AWS infrastructure.<\/strong><\/p>\n\n\n\n<p><strong>Resources:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Docs: <a href=\"https:\/\/docs.localstack.cloud\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/docs.localstack.cloud<\/a><\/li>\n\n\n\n<li>GitHub: <a href=\"https:\/\/github.com\/localstack\/localstack\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/localstack\/localstack<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>As a cloud consulting company, we&#8217;ve seen our share of AWS bills. But when our client forwarded us their monthly statement with a concerned email, even we were surprised. $3,247 for the month. The breakdown was what really caught our attention: They were literally spending more on building the product than running it in production. &#8230; <a title=\"Reducing Client AWS Costs by 70% with LocalStack\" class=\"read-more\" href=\"https:\/\/infivit.com\/blog\/localstack\/\" aria-label=\"Read more about Reducing Client AWS Costs by 70% with LocalStack\">Read more<\/a><\/p>\n","protected":false},"author":2,"featured_media":400,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[35],"tags":[22,36,38,37,39],"class_list":["post-370","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cloud","tag-aws","tag-aws-cost","tag-billing","tag-cloudcost","tag-localstack"],"_links":{"self":[{"href":"https:\/\/infivit.com\/blog\/wp-json\/wp\/v2\/posts\/370","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/infivit.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/infivit.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/infivit.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/infivit.com\/blog\/wp-json\/wp\/v2\/comments?post=370"}],"version-history":[{"count":6,"href":"https:\/\/infivit.com\/blog\/wp-json\/wp\/v2\/posts\/370\/revisions"}],"predecessor-version":[{"id":402,"href":"https:\/\/infivit.com\/blog\/wp-json\/wp\/v2\/posts\/370\/revisions\/402"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/infivit.com\/blog\/wp-json\/wp\/v2\/media\/400"}],"wp:attachment":[{"href":"https:\/\/infivit.com\/blog\/wp-json\/wp\/v2\/media?parent=370"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/infivit.com\/blog\/wp-json\/wp\/v2\/categories?post=370"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/infivit.com\/blog\/wp-json\/wp\/v2\/tags?post=370"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}