Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Automate AWS with boto3

By Kokil Thapa | Last reviewed: September 2026

Kubernetes cost optimization with Postmark is how small teams catch runaway cloud bills before finance does. EKS clusters hide waste in idle nodes, oversized requests, and untagged resources that Cost Explorer cannot attribute. Pairing boto3 automation with Postmark spend alerts turns that blind spot into a daily email you can act on. This guide keeps the production boto3 patterns from our AWS automation work and adds the FinOps layer I use when Laravel apps run on EKS for clients who need predictable NPR budgets.

If your team ships application code but treats infrastructure as a console chore, you are building debt that surfaces at month-end. Boto3 scripts make that work repeatable. Postmark delivers the alert when a staging namespace doubles its node count overnight. For broader app delivery, a Laravel developer in Nepal who understands EKS deploy pipelines saves you from bolting FinOps onto a broken architecture later. Start with credential hygiene, then wire cost signals into email your team actually reads.

How do you securely configure boto3 credentials for kubernetes cost automation?

Hardcoding AWS keys inside a cost-report script is the fastest way to leak credentials into Git history. Production automation must resolve credentials externally and run with least privilege.

Credential resolution order

Boto3 checks sources in a fixed chain. Scripts that work on your laptop often fail on EKS because the chain differs:

  1. Explicit parameters: Keys passed to the client constructor. Use only in local tests.
  2. Environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN for temporary creds.
  3. Shared credentials file: ~/.aws/credentials with named profiles for multi-account FinOps.
  4. IAM role: EC2 instance profile, ECS task role, or EKS IRSA. This is the production standard.
# Production-safe session for Cost Explorer queries
import boto3
from botocore.config import Config

session = boto3.Session(region_name='ap-south-1')  # Mumbai — low latency from Nepal

config = Config(
    retries={'max_attempts': 5, 'mode': 'adaptive'},
    connect_timeout=5,
    read_timeout=30
)

ce_client = session.client('ce', config=config)  # Cost Explorer API

For EKS workloads, bind an IAM role to the service account that runs your cost cron job. Grant only ce:GetCostAndUsage, ec2:DescribeInstances, and tag-read permissions. Never attach AdministratorAccess to a reporting pod. Local developers should use AWS SSO with short-lived tokens instead of long-lived access keys. The official boto3 credentials guide documents the full resolution order.

K8s Cost Alert PipelineEKS ClusterTagged pods/nodesboto3 ScriptCost Explorer APIThresholdCheck logicPostmarkSpend alert emailCommon waste sourcesIdle nodes · Oversized requests · Orphan EBSFix actionsScale down · Rightsize · Stop dev envs
Kubernetes cost optimization with Postmark: boto3 pulls AWS spend data, compares it to your budget, and emails the team when thresholds break

How do you query AWS costs for EKS with boto3?

Console Cost Explorer is fine for one-off checks. Scheduled kubernetes cost optimization with Postmark needs programmatic queries you can cron from a Lambda function or a Kubernetes CronJob.

Daily spend grouped by tag

Untagged EKS spend shows up as one blob. Tag nodes with Environment, Project, and Team at launch. Then filter Cost Explorer by those keys:

from datetime import datetime, timedelta, timezone

def get_daily_eks_spend(tag_key: str = 'Project') -> list[dict]:
    end = datetime.now(timezone.utc).date()
    start = end - timedelta(days=1)

    response = ce_client.get_cost_and_usage(
        TimePeriod={'Start': start.isoformat(), 'End': end.isoformat()},
        Granularity='DAILY',
        Metrics=['UnblendedCost'],
        Filter={
            'Dimensions': {
                'Key': 'SERVICE',
                'Values': ['Amazon Elastic Compute Cloud - Compute',
                           'Amazon Elastic Kubernetes Service']
            }
        },
        GroupBy=[{'Type': 'TAG', 'Key': tag_key}]
    )

    results = []
    for group in response['ResultsByTime'][0]['Groups']:
        amount = float(group['Metrics']['UnblendedCost']['Amount'])
        if amount > 0:
            results.append({'tag': group['Keys'][0], 'usd': amount})
    return results

Run this nightly in ap-south-1 if most of your Nepal-facing traffic lands there. Cross-check totals against the AWS billing console the first week. Small rounding differences are normal. Large gaps usually mean missing tags or resources in another region. See our FinOps cloud cost optimization basics for allocation strategies that work on small teams.

EC2 instance scheduling for non-production clusters

Dev and staging EKS node groups running 24/7 waste Rs 15,000–40,000/month (~USD 110–295) on typical three-node setups. Boto3 can stop them outside business hours:

def safe_stop_instances(instance_ids: list[str], dry_run: bool = True):
    ec2 = boto3.resource('ec2')
    instances = ec2.instances.filter(InstanceIds=instance_ids)

    for instance in instances:
        if instance.state['Name'] == 'running':
            print(f"Stopping {instance.id}")
            if not dry_run:
                instance.stop()
                instance.wait_until_stopped()
        else:
            print(f"Skipping {instance.id}: {instance.state['Name']}")

Filter by tag instead of hardcoded IDs. A tag like Schedule=office-hours keeps the script stable as nodes rotate. Pair this with the Kubernetes autoscaling guide so production clusters still scale under load.

What kubernetes settings cut AWS waste before boto3 runs?

Scripts cannot fix bad pod specs. Rightsize requests and limits first. Then automation maintains the gains.

Requests, limits, and the cluster autoscaler

CPU requests drive node count. A pod requesting 2 CPU but using 200m blocks an entire slot. Audit with kubectl top pods weekly. Set requests near p95 usage, not peak spikes.

  • Enable the cluster autoscaler only after requests reflect real usage.
  • Use Vertical Pod Autoscaler in recommendation mode before enabling auto-updates.
  • Set PodDisruptionBudgets so scale-down does not kill active sessions.
  • Install Kubecost or OpenCost if you need per-namespace attribution inside the cluster.

On a production Laravel booking app I maintain, fixing inflated memory requests dropped node count from five to three with no latency change. That is kubernetes cost optimization without touching application code.

Rightsizing ImpactBefore5 nodes · CPU req 2000m eachActual usage ~15% CPUMonthly: ~USD 450 EKS+EC2After3 nodes · CPU req 500m eachHPA handles traffic spikesMonthly: ~USD 270 EKS+EC240% savings from spec changes alone
Kubernetes cost optimization starts with accurate resource requests — boto3 scheduling and Postmark alerts maintain savings over time

How do you send Postmark alerts when kubernetes AWS spend exceeds budget?

Dashboards nobody opens do not prevent overruns. Postmark delivers structured HTML emails with open and bounce tracking. That fits kubernetes cost optimization with Postmark because finance and engineering both see the same number.

Postmark API call from your boto3 cron job

Store the Postmark server token in AWS Secrets Manager. Pull it at runtime. Never commit tokens to Git:

import json
import urllib.request

def send_cost_alert(postmark_token: str, daily_usd: float, threshold_usd: float):
    if daily_usd <= threshold_usd:
        return

    payload = {
        'From': 'finops@yourdomain.com',
        'To': 'team@yourdomain.com',
        'Subject': f'AWS spend alert: USD {daily_usd:.2f} exceeds USD {threshold_usd:.2f}',
        'HtmlBody': f'<p>Daily EKS+EC2 spend hit <strong>USD {daily_usd:.2f}</strong>.</p>'
                    f'<p>Check untagged resources and idle node groups.</p>'
    }

    req = urllib.request.Request(
        'https://api.postmarkapp.com/email',
        data=json.dumps(payload).encode(),
        headers={
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'X-Postmark-Server-Token': postmark_token
        },
        method='POST'
    )
    with urllib.request.urlopen(req, timeout=10) as resp:
        return json.loads(resp.read())

Laravel apps can use the same Postmark stream for transactional mail. Our Laravel mail with Postmark setup covers SPF, DKIM, and bounce handling. For provider comparison, read AWS SES vs Sendgrid vs Postmark. The Postmark send API documentation lists all message fields.

Alert thresholds that actually get action

Set three tiers instead of one flat limit:

  1. Warning at 80% of daily budget: Slack or email to the on-call engineer.
  2. Critical at 100%: Postmark email to engineering lead and finance contact.
  3. Weekly rollup: Sunday summary with top five services by spend.

Include deep links to Cost Explorer filtered views in the email body. A number without context gets ignored. A number plus "Project=legal-portal jumped 340%" gets fixed before lunch.

How do you handle S3 backups and pagination when automating AWS with boto3?

Orphan S3 objects and stale EBS snapshots quietly inflate kubernetes-adjacent bills. Backup automation must paginate and use multipart uploads for large files.

Pagination is mandatory

def list_all_objects(bucket_name: str, prefix: str = ''):
    s3_client = boto3.client('s3')
    paginator = s3_client.get_paginator('list_objects_v2')

    for page in paginator.paginate(Bucket=bucket_name, Prefix=prefix):
        for obj in page.get('Contents', []):
            yield {'key': obj['Key'], 'size': obj['Size']}

Use server-side Prefix filters. Client-side filtering after full listing wastes API calls and time on buckets with millions of keys. For off-site backup patterns, see automate off-site backups to S3.

Snapshot cleanup script

from datetime import datetime, timedelta, timezone

def cleanup_rds_snapshots(retention_days: int = 7):
    rds = boto3.client('rds')
    cutoff = datetime.now(timezone.utc) - timedelta(days=retention_days)

    paginator = rds.get_paginator('describe_db_snapshots')
    for page in paginator.paginate(SnapshotType='manual'):
        for snap in page['DBSnapshots']:
            if snap['SnapshotCreateTime'] < cutoff:
                rds.delete_db_snapshot(
                    DBSnapshotIdentifier=snap['DBSnapshotIdentifier']
                )

Validate JSON output from cost scripts with our JSON formatter before piping results into dashboards. Broken JSON in a CronJob log is a common silent failure.

S3 Multipart UploadBackup FilePart 1Part 2Part 3Parallel UploadAuto-retry partsResume on failureS3 BucketObject stored
Large etcd or database backups to S3 need multipart uploads — especially on Nepal links where mid-transfer drops are common

How do you implement resilient retry logic in boto3 cost scripts?

Cost Explorer throttles aggressive polling. Adaptive retry mode handles that better than the default three attempts.

from botocore.config import Config

resilient_config = Config(
    retries={'max_attempts': 10, 'mode': 'adaptive'}
)

client = boto3.Session().client('ce', config=resilient_config)

Retry throttling errors. Fail fast on AccessDeniedException. Log every retry with the API operation name. Silent retries hide undersized IAM policies until month-end. Track retry counts in CloudWatch as a leading indicator of API limit pressure.

boto3 Client vs Resource: Which interface fits kubernetes cost automation?

Cost Explorer, EKS, and RDS expose only Client APIs. EC2 stop/start scripts read cleaner with Resources.

CriteriaClientResource
API coverage100% including Cost Explorer and EKSSubset — no CE or EKS
Response formatRaw JSON dictsPython objects with methods
Paginationget_paginator()Collection iterators on supported services
Best for FinOpsCost queries, EKS describe, RDS snapshotsEC2 lifecycle, S3 uploads, IAM users

Mix both in one repo. Resources for readable EC2 scheduling. Clients for Cost Explorer and Postmark-trigger logic. For EKS setup fundamentals, start with run Kubernetes on AWS with Amazon EKS and kubernetes cost monitoring with Kubecost.

Client vs ResourceNew automation taskCost Explorer or EKS API?YesNoUse ClientUse ResourceEC2 · S3 · IAMBuilt-in waitersCE · EKS · RDSFull API coverage
FinOps scripts lean on boto3 Clients for Cost Explorer; Resources still suit EC2 stop/start automation

What production patterns tie boto3 automation to real client infrastructure?

Theory matters less than scripts that survive cron schedules and partial failures. These patterns recur across Laravel booking platforms on AWS and legal-tech portals with strict uptime needs.

Cross-region backup verification

def verify_cross_region_backups(source_region: str, target_region: str) -> bool:
    source_rds = boto3.client('rds', region_name=source_region)
    target_rds = boto3.client('rds', region_name=target_region)

    source_snaps = {
        s['DBSnapshotIdentifier']
        for p in source_rds.get_paginator('describe_db_snapshots').paginate()
        for s in p['DBSnapshots']
    }
    target_snaps = {
        s['SourceDBSnapshotIdentifier']
        for p in target_rds.get_paginator('describe_db_snapshots').paginate()
        for s in p['DBSnapshots']
        if s.get('SourceDBSnapshotIdentifier')
    }
    missing = source_snaps - target_snaps
    if missing:
        print(f'ALERT: {len(missing)} snapshots missing in {target_region}')
        return False
    return True

Run verification weekly. Trigger a Postmark alert on failure. Idempotency matters: the same script run twice should not delete twice or double-send unless state changed. Tag every resource at creation so cost scripts never depend on hardcoded ARN lists.

For teams without in-house DevOps, Linux system administration services and automation integration cover cron wiring, IAM roles, and alert tuning. Ongoing support and maintenance keeps scripts working after AWS API changes.

Key Takeaways

  • Tag EKS nodes and namespaces before querying Cost Explorer — untagged spend cannot be optimized.
  • Rightsize pod CPU and memory requests first; boto3 scheduling maintains savings on non-prod clusters.
  • Query daily spend with boto3 get_cost_and_usage and email threshold breaches through Postmark.
  • Use IAM roles and adaptive retries; never hardcode AWS keys or ignore API pagination.
  • Mix boto3 Clients for FinOps APIs with Resources for EC2 and S3 operational tasks.
  • Verify cross-region backups and snapshot retention with idempotent cron scripts, not console spot checks.

People Also Ask

What is kubernetes cost optimization with Postmark?

It combines Kubernetes resource tuning and AWS billing controls with Postmark email alerts when spend crosses your budget. Boto3 scripts query Cost Explorer daily and trigger emails your team will actually read, instead of relying on dashboards alone.

Can boto3 automate EKS node shutdown schedules?

Yes. Boto3 stops and starts EC2 instances backing EKS node groups based on tags like Schedule=office-hours. Verify pod disruption budgets first so scale-down does not drop active user sessions on staging clusters.

Why use Postmark instead of SNS for cost alerts?

Postmark delivers HTML email with open tracking, bounce handling, and consistent inbox placement. SNS works for pager-style pings, but finance stakeholders and managers expect readable email summaries with context and deep links.

How much can kubernetes cost optimization save on AWS?

Savings vary by cluster maturity. Rightsizing requests, stopping dev environments nights and weekends, and deleting orphan EBS volumes and snapshots commonly cut EKS-related spend 30–50% on small teams without reducing production capacity.

Build FinOps automation that survives production

Kubernetes cost optimization with Postmark plus boto3 gives you attribution, action, and accountability in one pipeline. Start with tags and pod requests this week. Add the Cost Explorer cron and Postmark alert next. Test every script in staging before it touches production node groups.

Need help wiring EKS cost alerts, auditing boto3 scripts, or connecting AWS automation to a Laravel app? Reach out to discuss your infrastructure automation needs or contact us for a FinOps review. For cloud-native PHP architecture, see our guide on serverless Laravel on AWS Lambda. Additional tactics live in 12 practical AWS bill reduction tactics and deploying Laravel on AWS EC2 with RDS.

Frequently Asked Questions

Boto3 is the official AWS SDK for Python. It provides programmatic access to AWS services via API calls, enabling infrastructure automation, batch processing, and custom tooling without using the console or CLI manually.

Boto3 itself is free. You pay only for underlying AWS API requests and provisioned resources. Budget Rs 500–2,000 (USD 4–15) monthly for light automation scripts running on small EC2 instances or Lambda invocations.

Use boto3 for custom logic, conditional workflows, or data processing between AWS calls. Prefer Terraform for declarative infrastructure state management and AWS CLI for simple one-off administrative commands without programming overhead.

Never hardcode keys. Use IAM roles attached to EC2 instances or Lambda execution roles for automatic credential rotation. For local development, use AWS SSO or named profiles in ~/.aws/credentials. In my experience deploying Python automation tools on Ubuntu servers, assuming an IAM role via STS is safer than storing long-lived access keys in environment variables or config files that might accidentally get committed to Git repositories.

No. While boto3 covers most services, newer or niche AWS features may have incomplete SDK support or require botocore updates. Always check the boto3 documentation for specific service coverage before architecting around it. On real client projects integrating AWS with Laravel applications, I have encountered gaps where CloudFormation custom resources or direct HTTP signing were necessary because the high-level boto3 resource interface lacked required parameters for certain RDS or Cognito configurations.

Always use paginators instead of manual NextToken loops. The client.get_paginator('list_objects_v2').paginate() method handles token management automatically and prevents missing records. Failing to paginate is a common bug I see in audit scripts; a single list call returns maximum 1,000 items silently. For large S3 buckets or DynamoDB tables containing millions of records, proper pagination ensures your automation script processes every object rather than just the first page of results.

Pin exact versions of boto3 and botocore in requirements.txt since AWS frequently releases breaking changes in minor versions. Use virtual environments to isolate dependencies from system Python. When maintaining multiple automation scripts across different client projects, I have seen unpinned boto3 upgrades break existing code due to deprecated parameters or changed response structures. Running pip install boto3==1.34.12 specifically prevents unexpected failures during server provisioning or scheduled cron job executions.

Configure botocore.Config with max_attempts and retry_mode='adaptive' when creating clients. This handles throttling, connection errors, and temporary service unavailability automatically. Without retries, automation scripts fail during peak hours or when hitting rate limits. On production systems processing thousands of SES emails or S3 uploads daily, adaptive retry mode with exponential backoff has prevented countless false-positive alerting incidents caused by momentary AWS API congestion or network blips between Kathmandu and us-east-1 regions.

Not directly. Boto3 is synchronous. Use aioboto3 as a third-party async wrapper or run boto3 calls in asyncio.to_thread() to avoid blocking event loops. For FastAPI or Django ASGI applications requiring AWS integration, wrapping synchronous boto3 calls prevents request handler starvation. I have used this pattern in Laravel-adjacent Python microservices where async HTTP handlers needed to query DynamoDB or invoke Lambda functions without degrading overall application throughput during concurrent user requests.

Use moto library to mock AWS services locally. Moto intercepts boto3 calls and simulates responses without network access or costs. Write unit tests covering success paths, error handling, and edge cases before deploying. Testing against real AWS during development burns money and risks modifying production resources accidentally. On legal-tech portals handling sensitive document workflows, comprehensive moto-based test suites ensured S3 upload logic and SES notification triggers worked correctly before touching any live customer data environments.

Overly permissive IAM policies, logging sensitive data, disabling SSL verification, and storing credentials in source control are frequent issues. Apply least-privilege principles using IAM Access Analyzer. Enable CloudTrail logging for all automated actions. Review boto3 code for hardcoded secrets using tools like detect-secrets before committing. In Nepal-based projects where teams share development environments, enforcing pre-commit hooks scanning for AWS patterns has prevented multiple credential exposure incidents that could have compromised client billing accounts or exposed PII.

Use transfer_config for parallel S3 uploads, batch_writer for DynamoDB, and connection pooling via botocore.Config(max_pool_connections=50). Reuse clients instead of creating new ones per operation. Process items concurrently with ThreadPoolExecutor for I/O-bound tasks. On eCommerce platforms syncing product catalogs between WooCommerce and AWS OpenSearch, tuning these parameters reduced nightly sync windows from four hours to forty minutes by maximizing available bandwidth and eliminating redundant TCP handshakes during bulk indexing operations.

Yes. Boto3 runs in any Python environment and can be invoked from PHP via shell_exec, subprocess, or dedicated queue workers. For tight integration, expose boto3 logic through a FastAPI endpoint consumed by Laravel. On projects like Nepal Gift Card, Python automation scripts triggered via Laravel queues handled complex S3 media processing and SES transactional email batching separately from the main PHP application, keeping response times fast while leveraging boto3's superior AWS ecosystem coverage for specialized tasks.

Emit structured logs to CloudWatch Logs, create metrics via PutMetricData, and set up alarms for failures or latency spikes. Add X-Ray tracing for distributed visibility. Tag all automated resources consistently. Without observability, silent failures accumulate undetected until customers report missing data or broken workflows. Maintaining dozens of automation scripts across shared EC2 infrastructure, centralized CloudWatch dashboards with error-rate alerts have been essential for catching credential expirations, quota breaches, and upstream API changes before they impact business operations.

Consider AWS CDK or Pulumi for infrastructure-as-code with programming languages, Step Functions for orchestrated serverless workflows, or EventBridge for event-driven automation without custom code. For simple tasks, Systems Manager Automation documents eliminate SDK maintenance entirely. Choose based on complexity, team skills, and operational burden. Sometimes a managed AWS service solves the problem better than custom boto3 scripts, reducing long-term maintenance costs and freeing developer time for higher-value feature work on client applications.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: