AWS Lightsail Setup: 13 Steps, $5/Mo, 60 Min [2026]

AWS Lightsail is the one corner of Amazon Web Services where a flat $5 a month buys a whole virtual server. CloudMart’s October 20, 2026 check against the AWS Price List API lists that plan as 512 MB of RAM, 2 vCPUs, 20 GB of SSD and 1 TB of monthly transfer. This AWS Lightsail setup tutorial turns that plan into a locked-down Linux web server in 13 steps, and it takes about 60 minutes if you paste the commands as written.

You will finish with an Ubuntu instance on a Lightsail static IP, a firewall that only admits you on SSH, an app behind Nginx with HTTPS, daily automatic snapshots, CPU alarms, and a second instance behind a Lightsail load balancer. Every step runs from the AWS CLI, so you can save the commands as a script and rebuild the whole stack later. If you are still choosing a provider, our DigitalOcean vs Vultr vs Lightsail comparison covers the IOPS and price differences before you commit.

One warning up front. Lightsail prices changed shape during 2026, and AWS’s own pages disagree on the entry plan (more on that below). Treat every price here as a snapshot from October 25, 2026, and confirm it in the console before you click Create.

Google · Preferred Sources

Don't miss new tech stories on Google

Add Tech Insider once in the Google app and our stories appear in your news suggestions.

Add Now

What You Will Build With AWS Lightsail

The finished project has six moving parts, and each one maps to a step you can test on its own. A Linux instance named web-01 runs Ubuntu, Nginx, and a 40-line Python service. A static IP keeps the address stable across reboots and rebuilds. The built-in Lightsail firewall opens ports 80 and 443 to the world and port 22 only to your current address.

On top of that sits a DNS zone with an A record, a Let’s Encrypt certificate on the instance, and an automatic snapshot schedule. The last stage adds web-02 and a Lightsail load balancer with a free managed certificate, so a failed instance no longer takes the site down.

Why bother with Lightsail instead of EC2? The 2025 Stack Overflow Developer Survey found that 43.3% of respondents use AWS, which means most teams already hold an AWS account. Lightsail lets them run a small site, a staging box, a hobby API, or a game server without learning VPCs, security groups, and Elastic IP billing rules first. Our Palworld cloud server walkthrough uses the same pattern for a private game host.

Output blocks in this tutorial show representative results, so IDs, timestamps, and IP addresses will differ on your account.

The trade-off is control. Lightsail hides the VPC, caps you at fixed plan sizes, and exposes fewer knobs than EC2. For one to five servers that is a feature. For fifty it becomes a ceiling, and a later section covers when to move on.

StepWhat you doTime
———
1Install the AWS CLI and create a scoped profile8 min
2Pick a region, plan and blueprint4 min
3Generate an Ed25519 SSH key2 min
4Create the instance with a launch script6 min
5Attach a static IP2 min
6Lock the Lightsail firewall4 min
7Harden SSH and add swap6 min
8Deploy the app behind Nginx7 min
9Create the DNS zone and A record5 min
10Issue the HTTPS certificate4 min
11Enable automatic snapshots and test a restore6 min
12Add CPU alarms with email alerts4 min
13Put two instances behind a load balancer12 min

What AWS Lightsail Costs in October 2026

Lightsail bills each instance by the hour, capped at the plan’s monthly price, and the plan bundles RAM, vCPUs, SSD storage, and a transfer allowance. That cap is the whole appeal. A forgotten instance can never cost more than its monthly number, unlike a forgotten EC2 box with a fat EBS volume and a public IPv4 charge.

AWS’s pricing page, as fetched this week, lists general-purpose Linux plans at $5, $7, $12, $24, and $44 a month, with larger plans running up to $1,764. CloudMart’s ladder adds the 1 GB, 2 GB, 4 GB, and 8 GB specs for those middle tiers and notes that the $7 plan now ships 2 vCPUs and 40 GB of SSD. Older comparison posts still list the $7 plan with 1 vCPU and 20 GB, so check the date on anything you read.

Here is the catch with the entry price. AWS’s Lightsail FAQ still advertises a plan from $0.0047 an hour, capped at $3.50 a month, and Surfercloud’s October 2026 benchmark repeats $3.50 for Lightsail’s Linux entry tier. The pricing page shows $5 for the smallest public-IPv4 general-purpose plan. The sources I checked do not explain the gap, so budget $5 and treat anything lower as a bonus.

The table below uses only line items AWS publishes on its own pricing pages. The totals are plain addition.

Line itemPriceNotes
———
Entry general-purpose instance (public IPv4)$5 per monthHourly billing, capped at the monthly price
Second identical instance$5 per monthNeeded for the load-balancer stage
Lightsail load balancer$18 per monthFree managed TLS certificates, AWS handles renewal
Static IP, attached to an instance$0One account can hold five
Static IP, unattached$0.005 per hourBilled only after the address has been unattached for more than one hour
Automatic snapshotsFree to enableYou pay for the snapshot storage
Block storage$0.10 per GB per monthStarts at 8 GB
Outbound transfer past the plan allowanceFrom $0.09 per GBVaries by region
Container service (for comparison)$7 per monthIncludes a 500 GB monthly transfer quota

For the finished project, two instances plus the load balancer come to $28 a month before snapshot storage. AWS’s own worked example for two instances and 100 GB of overage lands at $19 a month, which shows how fast $0.09 a gigabyte adds up when traffic spikes. Snapshots, by AWS’s published rate, cost $0.05 per GB-month, so a 20 GB snapshot adds roughly $1 a month.

Prerequisites and Tools (Steps 1 and 2)

You need an AWS account with billing enabled, a Linux or macOS shell (Windows users can use WSL2), and a domain name you control. The domain is optional until step 9. Everything before that works with the raw static IP.

ToolVersionWhy
———
AWS CLI2.x (current v2 build)Runs every Lightsail command
OpenSSH client8.x or newerEd25519 keys and modern defaults
jqNot listedParsing CLI output
dig (dnsutils)anyChecking DNS propagation
Ubuntu LTS blueprintnewest LTS Lightsail offersStep 2 shows how to list it
Python3.x from the Ubuntu imageRuns the demo app, standard library only

The same cloud tooling shows up across our cloud computing coverage, so skim it if the AWS CLI is new to you.

Step 1: Install the AWS CLI and Create a Scoped Profile

Download and install AWS CLI v2. The installer is a single zip file and needs no Python on your machine.

curl -s "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o awscliv2.zip
unzip -q awscliv2.zip
sudo ./aws/install
aws --version

The last command should print something like this:

aws-cli/2.x.x Python/3.x.x Linux/6.8.0 exe/x86_64.ubuntu.24

Next, create an IAM user or an IAM Identity Center permission set that can only touch Lightsail. Do not run this tutorial with root credentials. A minimal policy looks like this:

{
  "Version": "2012-10-17",
  "Statement": [
    { "Effect": "Allow", "Action": "lightsail:*", "Resource": "*" }
  ]
}

Then store the credentials under a named profile and export the variables the rest of the tutorial uses:

aws configure --profile lightsail-admin
export AWS_PROFILE=lightsail-admin
export AWS_REGION=us-east-1
export AZ=${AWS_REGION}a
export NAME=web-01
aws sts get-caller-identity

If get-caller-identity prints your account ID, the profile works. The most common failure here is a typo in the secret key, which shows up as InvalidClientTokenId.

Step 2: Pick a Region, Plan, and Blueprint

Pick the region closest to your users. Lightsail added instances, managed databases, containers, and load balancers in Asia Pacific (Hong Kong), South America (Sao Paulo), and Europe (Spain) on June 12, 2026, according to AWS release notes, so the map is wider than it was a year ago. List what your account can use:

aws lightsail get-regions --query 'regions[].name' --output text
aws lightsail get-blueprints \
  --query "blueprints[?starts_with(blueprintId,'ubuntu')].[blueprintId,name,version]" \
  --output table
aws lightsail get-bundles \
  --query "bundles[?supportedPlatforms[0]=='LINUX_UNIX'].[bundleId,price,ramSizeInGb,cpuCount,diskSizeInGb,transferPerMonthInGb]" \
  --output table

Sample output, trimmed. Your IDs and the newest Ubuntu release may differ:

-------------------------------------------
|              GetBlueprints              |
+--------------+------------+-------------+
|  ubuntu_24_04|  Ubuntu    |  24.04 LTS  |
+--------------+------------+-------------+
-------------------------------------------------------
|                     GetBundles                      |
+------------+--------+------+------+-------+----------+
|  nano_3_0  |  5.0   | 0.5  |  2   |  20   |  1024    |
|  micro_3_0 |  7.0   | 1.0  |  2   |  40   |  2048    |
|  small_3_0 |  12.0  | 2.0  |  2   |  60   |  3072    |
+------------+--------+------+------+-------+----------+

Copy the newest Ubuntu LTS ID and the bundle ID that matches the $5 plan into variables. Skip any bundle whose ID contains ipv6, because a Lightsail static IP cannot attach to an IPv6-only instance, per AWS’s FAQ.

export BLUEPRINT=ubuntu_24_04
export BUNDLE=nano_3_0

AWS also launched compute-optimized bundles in April 2026, with up to 72 vCPUs, so CPU-bound jobs no longer have to jump to EC2. A web server on the $5 plan does not need them.

Create the Instance, Static IP, and Firewall (Steps 3 to 6)

These four steps produce a reachable server. Order matters here, because the launch script in step 4 embeds the SSH key from step 3, and the firewall in step 6 should close SSH to the public internet before you do any real work on the box.

Step 3: Generate an Ed25519 SSH Key

Create a dedicated key for this project. A passphrase is worth the five seconds it costs.

ssh-keygen -t ed25519 -a 100 -f ~/.ssh/lightsail_ed25519 -C "lightsail-web"
chmod 600 ~/.ssh/lightsail_ed25519
cat ~/.ssh/lightsail_ed25519.pub

The public key is one line starting with ssh-ed25519. The tutorial injects it through the launch script rather than importing it into Lightsail, which keeps the key pair out of your account-level key list.

Step 4: Create the Instance With a Launch Script

A Lightsail launch script is a shell script that runs once as root on first boot. This one adds your key, patches the system, and installs Nginx, fail2ban, and unattended upgrades before you ever log in.

PUBKEY=$(cat ~/.ssh/lightsail_ed25519.pub)
cat > launch.sh <<EOF
#!/bin/bash
set -eux
echo "$PUBKEY" >> /home/ubuntu/.ssh/authorized_keys
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get upgrade -y
apt-get install -y nginx fail2ban unattended-upgrades python3
systemctl enable --now nginx fail2ban
EOF

aws lightsail create-instances \
  --instance-names "$NAME" \
  --availability-zone "$AZ" \
  --blueprint-id "$BLUEPRINT" \
  --bundle-id "$BUNDLE" \
  --user-data file://launch.sh \
  --tags key=project,value=ls-demo

The API answers with an operation record whose status reads Started. Poll the instance until it reports running:

aws lightsail get-instance-state --instance-name "$NAME"
{
    "state": {
        "code": 16,
        "name": "running"
    }
}

Code 16 means running, but the launch script keeps working for another minute or two. Do not SSH in until the script finishes, or you will race apt for its lock. A quick way to wait is to retry ssh until nginx answers on port 80 in step 6.

Step 5: Attach a Lightsail Static IP

An instance’s default public IP changes whenever you stop and start it. A Lightsail static IP does not. Allocate one and attach it:

aws lightsail allocate-static-ip --static-ip-name web-01-ip
aws lightsail attach-static-ip --static-ip-name web-01-ip --instance-name "$NAME"
export IP=$(aws lightsail get-static-ip --static-ip-name web-01-ip \
  --query 'staticIp.ipAddress' --output text)
echo "$IP"
203.0.113.25

(That address is from the documentation range, yours will differ.) An attached static IP carries no charge, and one account can hold five. The trap is the unattached case: AWS bills $0.005 per hour for a static IP that sits unattached for more than an hour, so release addresses you stop using with release-static-ip.

Step 6: Lock the Lightsail Firewall

Every Lightsail instance has a built-in firewall that filters by source IP, port, and protocol. New instances start with SSH and HTTP open to the whole internet. Replace that with rules that fit this project. SSH accepts only your current address plus the browser-based SSH client in the Lightsail console, and web ports stay open.

MYIP=$(curl -s https://checkip.amazonaws.com)
cat > ports.json <<EOF
[
  {"fromPort":22,"toPort":22,"protocol":"tcp",
   "cidrs":["${MYIP}/32"],"cidrListAliases":["lightsail-connect"]},
  {"fromPort":80,"toPort":80,"protocol":"tcp",
   "cidrs":["0.0.0.0/0"],"ipv6Cidrs":["::/0"]},
  {"fromPort":443,"toPort":443,"protocol":"tcp",
   "cidrs":["0.0.0.0/0"],"ipv6Cidrs":["::/0"]}
]
EOF
aws lightsail put-instance-public-ports \
  --instance-name "$NAME" --port-infos file://ports.json
aws lightsail get-instance-port-states --instance-name "$NAME" \
  --query 'portStates[].[fromPort,protocol,cidrs[0],state]' --output table

Read that last output carefully. put-instance-public-ports replaces the whole rule set, so any port you leave out of the JSON closes. That is the behavior you want here, and it is also the number one cause of “I locked myself out” tickets.

Test from your own machine. Port 80 should answer with the default Nginx page, and the same request from a phone on mobile data should still work:

curl -sI "http://$IP" | head -n 1
HTTP/1.1 200 OK

A Lightsail firewall only filters traffic to this one instance. If you need rules across many hosts or a shared appliance, the open-source approach in our pfSense firewall setup guide is a different tool for a different job.

Harden the Server and Deploy the App (Steps 7 and 8)

The instance is reachable and filtered, but the operating system still ships with defaults. These two steps tighten SSH, add swap so the 512 MB plan survives a package install, and put a real application behind Nginx.

Step 7: Harden SSH and Add Swap

Log in with the key from step 3. The default user on Ubuntu blueprints is ubuntu.

ssh -i ~/.ssh/lightsail_ed25519 ubuntu@"$IP"

Once you are in, write an SSH drop-in file. Ubuntu reads everything in /etc/ssh/sshd_config.d/ before the main file, so this survives package upgrades.

sudo tee /etc/ssh/sshd_config.d/10-hardening.conf >/dev/null <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
MaxAuthTries 3
LoginGraceTime 20
AllowUsers ubuntu
EOF
sudo sshd -t && sudo systemctl reload ssh

Always run sshd -t first. A syntax error plus a reload equals a dead SSH daemon, and your only way back is the browser-based console or a snapshot restore. Keep your current session open and test a second login from another terminal before you close it.

Next, turn on unattended security upgrades and give the small plan some swap. A 512 MB instance will kill apt or pip with an out-of-memory error without it.

sudo tee /etc/apt/apt.conf.d/20auto-upgrades >/dev/null <<'EOF'
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
EOF

sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -h
               total        used        free      shared  buff/cache   available
Mem:           457Mi       168Mi        62Mi       1.0Mi       226Mi       289Mi
Swap:          1.0Gi          0B       1.0Gi

The fail2ban package from the launch script already protects SSH with its default jail. For a deeper look at host defenses, our ClamAV on-access scanning guide shows how to add malware scanning to a Linux server that accepts uploads.

Step 8: Deploy the App Behind Nginx

The demo app is a 25-line Python service that returns JSON with the hostname and uptime. It uses only the standard library, so there is nothing to install and nothing to patch. Save it on your workstation as deploy-app.sh, which you will reuse for the second instance in step 13.

#!/bin/bash
# deploy-app.sh: run on the server as root via: ssh host 'sudo bash -s' < deploy-app.sh
set -eux
mkdir -p /opt/ls-demo
cat > /opt/ls-demo/app.py <<'PYEOF'
import json, socket, time
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

START = time.time()

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/healthz":
            body, ctype = b"ok", "text/plain"
        else:
            body = json.dumps({
                "host": socket.gethostname(),
                "uptime_s": int(time.time() - START),
                "path": self.path,
            }).encode()
            ctype = "application/json"
        self.send_response(200)
        self.send_header("Content-Type", ctype)
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

ThreadingHTTPServer(("127.0.0.1", 8000), Handler).serve_forever()
PYEOF

cat > /etc/systemd/system/ls-demo.service <<'UNITEOF'
[Unit]
Description=Lightsail demo app
After=network.target

[Service]
DynamicUser=yes
ExecStart=/usr/bin/python3 /opt/ls-demo/app.py
Restart=on-failure
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes

[Install]
WantedBy=multi-user.target
UNITEOF

cat > /etc/nginx/sites-available/ls-demo <<'NGXEOF'
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
NGXEOF

ln -sf /etc/nginx/sites-available/ls-demo /etc/nginx/sites-enabled/ls-demo
rm -f /etc/nginx/sites-enabled/default
nginx -t
systemctl daemon-reload
systemctl enable --now ls-demo
systemctl reload nginx

From your workstation, push it to the server and test:

ssh -i ~/.ssh/lightsail_ed25519 ubuntu@"$IP" 'sudo bash -s' < deploy-app.sh
curl -s "http://$IP/"
curl -s "http://$IP/healthz"
{"host": "ip-172-26-9-14", "uptime_s": 41, "path": "/"}
ok

If you see 502 Bad Gateway instead, the Python service is not running. Run journalctl -u ls-demo -n 30 on the server and read the last lines. The /healthz route matters later, because the load balancer in step 13 uses it to decide whether an instance is alive.

Add DNS and HTTPS (Steps 9 and 10)

Browsers distrust plain HTTP and certificate authorities will not issue a certificate for a bare IP. So the project needs a hostname. These two steps create the DNS records in Lightsail and then request a free Let’s Encrypt certificate.

Step 9: Create the Lightsail DNS Zone

Lightsail can host your DNS zone for free. AWS’s FAQ reportedly allows up to six zones per account. One catch trips people up: the domain commands only work in us-east-1, even when your instance lives elsewhere. The examples below use example.com, so swap in your own domain.

aws lightsail create-domain --region us-east-1 --domain-name example.com

aws lightsail create-domain-entry --region us-east-1 \
  --domain-name example.com \
  --domain-entry "name=app.example.com,type=A,target=$IP"

aws lightsail get-domain --region us-east-1 --domain-name example.com \
  --query "domain.domainEntries[?type=='NS'].target" --output text

The last command prints four name servers. Copy them into your registrar’s name-server settings, replacing the registrar’s defaults. If your DNS lives elsewhere, skip this step and create the A record there instead. Then check propagation:

dig +short app.example.com
203.0.113.25

Propagation can take minutes or hours. Do not move to step 10 until dig returns your static IP, because Let’s Encrypt validates over the public internet and a stale record fails the challenge.

Step 10: Issue the HTTPS Certificate

Certbot’s snap package is the install method Let’s Encrypt recommends for Ubuntu. Run it on the server:

sudo snap install --classic certbot
sudo ln -sf /snap/bin/certbot /usr/bin/certbot
sudo sed -i 's/server_name _;/server_name app.example.com;/' /etc/nginx/sites-available/ls-demo
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d app.example.com \
  -m [email protected] --agree-tos --no-eff-email --redirect -n
sudo certbot renew --dry-run
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/app.example.com/fullchain.pem
Congratulations! You have successfully enabled HTTPS on https://app.example.com
...
Congratulations, all simulated renewals succeeded

Snap installs a systemd timer that renews the certificate twice a day, so the dry run is your proof that renewal works. Confirm the redirect and the certificate from outside:

curl -sI http://app.example.com | head -n 3
curl -sI https://app.example.com | head -n 1

Remember the --redirect flag. It is right for a single server but needs undoing behind the load balancer later, and step 13 shows how to undo it.

Protect It With Snapshots and Alarms (Steps 11 and 12)

A server you cannot restore is a liability. These steps add Lightsail snapshots, prove that a restore works, and wire up alerts so a runaway process emails you before the site stops answering.

Step 11: Enable Lightsail Snapshots and Test a Restore

Enabling automatic snapshots costs nothing by itself. You pay only for the snapshot storage. Set the daily time in UTC, then take a manual snapshot before any risky change:

aws lightsail enable-add-on --resource-name "$NAME" \
  --add-on-request "addOnType=AutoSnapshot,autoSnapshotAddOnRequest={snapshotTimeOfDay=06:00}"

aws lightsail create-instance-snapshot \
  --instance-name "$NAME" \
  --instance-snapshot-name "web-01-baseline-$(date +%Y%m%d)"

aws lightsail get-instance-snapshots \
  --query 'instanceSnapshots[].[name,state,sizeInGb,createdAt]' --output table

AWS documents seven retained automatic snapshots at $0.05 per GB-month, according to the current FAQ. Snapshots of a 20 GB disk therefore stay in the low single dollars per month. Wait until the state reads available, then prove the restore path with a throwaway instance:

aws lightsail create-instances-from-snapshot \
  --instance-names web-01-restore \
  --availability-zone "$AZ" \
  --instance-snapshot-name "web-01-baseline-$(date +%Y%m%d)" \
  --bundle-id "$BUNDLE"

Restoring creates a new instance with a new public IP. Your static IP stays on web-01. For a real recovery you would detach the static IP from the broken instance and attach it to the restored one. Delete the test instance once you have confirmed that Nginx answers on its temporary address, since it bills hourly:

aws lightsail delete-instance --instance-name web-01-restore

This restore flow is also how you resize. To move from the $5 plan to a bigger one, snapshot the instance and create a new one from that snapshot with a larger --bundle-id. Lightsail only lets you go up, never down.

Step 12: Add CPU Alarms With Email Alerts

Lightsail exposes instance metrics for free and lets you attach alarms to them. First register an email contact method and confirm the link AWS mails you. Then create two alarms: sustained high CPU and burst capacity running low. The burst alarm matters on small plans, because a Lightsail instance that exhausts its CPU burst gets throttled hard.

aws lightsail create-contact-method --protocol Email \
  --contact-endpoint [email protected]

aws lightsail put-alarm --alarm-name web-01-cpu-high \
  --monitored-resource-name "$NAME" --metric-name CPUUtilization \
  --comparison-operator GreaterThanOrEqualToThreshold \
  --threshold 80 --evaluation-periods 3 --datapoints-to-alarm 3 \
  --period 300 --treat-missing-data breaching \
  --contact-protocols Email --notification-triggers ALARM OK \
  --notification-enabled

aws lightsail put-alarm --alarm-name web-01-burst-low \
  --monitored-resource-name "$NAME" --metric-name BurstCapacityPercentage \
  --comparison-operator LessThanOrEqualToThreshold \
  --threshold 20 --evaluation-periods 2 --datapoints-to-alarm 2 \
  --period 300 --treat-missing-data notBreaching \
  --contact-protocols Email --notification-triggers ALARM OK \
  --notification-enabled

Check that metrics flow and read the last hour of CPU data:

aws lightsail get-instance-metric-data --instance-name "$NAME" \
  --metric-name CPUUtilization --period 300 \
  --start-time $(date -u -d '-1 hour' +%s) --end-time $(date -u +%s) \
  --unit Percent --statistics Average \
  --query 'metricData[].[timestamp,average]' --output table

An idle Nginx box sits near 1 to 3 percent. To prove the alarm fires, run yes > /dev/null & on the server for ten minutes and watch your inbox. Then kill the job.

Scale Out With a Lightsail Load Balancer (Step 13)

One instance is a single point of failure. A Lightsail load balancer spreads traffic across instances in the same region, runs health checks, and terminates HTTPS with a free managed certificate that AWS renews for you. It costs $18 a month on top of the instances. If you want to see how it stacks up against the heavier options, our ALB vs NLB vs Cloudflare load balancing comparison puts the pricing side by side.

Step 13: Build the Second Instance and the Load Balancer

First create web-02 in the same availability zone with the same launch script, then deploy the app with the script from step 8. It needs no static IP, because the load balancer reaches it by instance name.

aws lightsail create-instances --instance-names web-02 \
  --availability-zone "$AZ" --blueprint-id "$BLUEPRINT" \
  --bundle-id "$BUNDLE" --user-data file://launch.sh \
  --tags key=project,value=ls-demo

aws lightsail put-instance-public-ports --instance-name web-02 \
  --port-infos file://ports.json

Wait about two minutes, find the instance’s temporary IP, and deploy:

IP2=$(aws lightsail get-instance --instance-name web-02 \
  --query 'instance.publicIpAddress' --output text)
ssh -i ~/.ssh/lightsail_ed25519 ubuntu@"$IP2" 'sudo bash -s' < deploy-app.sh

Now create the load balancer. The instance port is 80 and the health check hits the /healthz route from step 8.

aws lightsail create-load-balancer --load-balancer-name web-lb \
  --instance-port 80 --health-check-path /healthz

aws lightsail attach-instances-to-load-balancer \
  --load-balancer-name web-lb --instance-names web-01 web-02

aws lightsail create-load-balancer-tls-certificate \
  --load-balancer-name web-lb --certificate-name web-lb-cert \
  --certificate-domain-name app.example.com

The certificate request returns validation records. AWS’s SDK documentation describes the LoadBalancerTlsCertificate object that carries them. Pull the CNAME name and value, add them to your Lightsail DNS zone, and wait for validation:

aws lightsail get-load-balancer-tls-certificates --load-balancer-name web-lb \
  --query 'tlsCertificates[0].[status,domainValidationRecords[0].name,domainValidationRecords[0].value]' \
  --output text
PENDING_VALIDATION   _a1b2c3.app.example.com.   _d4e5f6.acm-validations.aws.

Create that CNAME in the zone, wait until the status flips to ISSUED, and then attach the certificate and turn on the HTTP to HTTPS redirect at the load balancer:

aws lightsail attach-load-balancer-tls-certificate \
  --load-balancer-name web-lb --certificate-name web-lb-cert
aws lightsail update-load-balancer-attribute \
  --load-balancer-name web-lb --attribute-name HttpsRedirection \
  --attribute-value true

Before you flip DNS, remove the redirect from the instances, since the load balancer provides its own integrated certificate management. Reset the site config on both instances:

for H in "$IP" "$IP2"; do
  ssh -i ~/.ssh/lightsail_ed25519 ubuntu@"$H" 'sudo bash -s' < deploy-app.sh
done
ssh -i ~/.ssh/lightsail_ed25519 ubuntu@"$IP" \
  'sudo certbot delete --cert-name app.example.com -n'

Re-running deploy-app.sh rewrites the Nginx site to the plain-HTTP version from step 8, which drops the Certbot edits. The certbot delete line removes the instance certificate, so its renewal timer stops trying to validate a hostname that now points at the load balancer. Finally, point the hostname at the load balancer using an alias record and watch the targets turn healthy:

LBDNS=$(aws lightsail get-load-balancer --load-balancer-name web-lb \
  --query 'loadBalancer.dnsName' --output text)

aws lightsail delete-domain-entry --region us-east-1 --domain-name example.com \
  --domain-entry "name=app.example.com,type=A,target=$IP"
aws lightsail create-domain-entry --region us-east-1 --domain-name example.com \
  --domain-entry "name=app.example.com,type=A,target=$LBDNS,isAlias=true"

aws lightsail get-load-balancer-target-health --load-balancer-name web-lb \
  --query 'targetHealthDescriptions[].[target.id,targetHealth.state]' --output table
-------------------------
|  web-01  |  healthy   |
|  web-02  |  healthy   |
-------------------------

Prove that traffic alternates between the two servers:

for i in 1 2 3 4 5 6; do curl -s https://app.example.com/ | jq -r .host; done
ip-172-26-9-14
ip-172-26-3-201
ip-172-26-9-14
ip-172-26-3-201
ip-172-26-3-201
ip-172-26-9-14

Stop ls-demo on one server and repeat the loop. Within a minute or so every response should come from the survivor. That is the payoff for the extra $18 a month.

The Complete Working Project in One Script

Steps 3 to 6, 11 and 12 are pure CLI work, so they collapse into one repeatable script. Save the following as provision.sh next to launch.sh, ports.json, and deploy-app.sh. It creates the instance, waits for it, attaches the static IP, closes the firewall, enables daily snapshots, and registers the CPU alarm. Run it with a different NAME and you get a second environment in about four minutes.

#!/bin/bash
set -euo pipefail
export AWS_PROFILE=${AWS_PROFILE:-lightsail-admin}
export AWS_REGION=${AWS_REGION:-us-east-1}
AZ=${AWS_REGION}a
NAME=${1:-web-01}
BLUEPRINT=${BLUEPRINT:-ubuntu_24_04}
BUNDLE=${BUNDLE:-nano_3_0}
EMAIL=${ALERT_EMAIL:?set ALERT_EMAIL}

aws lightsail create-instances --instance-names "$NAME" \
  --availability-zone "$AZ" --blueprint-id "$BLUEPRINT" \
  --bundle-id "$BUNDLE" --user-data file://launch.sh >/dev/null

until [ "$(aws lightsail get-instance-state --instance-name "$NAME" \
  --query state.name --output text)" = running ]; do sleep 5; done

aws lightsail create-contact-method --protocol Email \
  --contact-endpoint "$EMAIL" >/dev/null || true
aws lightsail allocate-static-ip --static-ip-name "$NAME-ip" >/dev/null
aws lightsail attach-static-ip --static-ip-name "$NAME-ip" \
  --instance-name "$NAME" >/dev/null
aws lightsail put-instance-public-ports --instance-name "$NAME" \
  --port-infos file://ports.json >/dev/null
aws lightsail enable-add-on --resource-name "$NAME" \
  --add-on-request "addOnType=AutoSnapshot,autoSnapshotAddOnRequest={snapshotTimeOfDay=06:00}" >/dev/null
aws lightsail put-alarm --alarm-name "$NAME-cpu-high" \
  --monitored-resource-name "$NAME" --metric-name CPUUtilization \
  --comparison-operator GreaterThanOrEqualToThreshold --threshold 80 \
  --evaluation-periods 3 --datapoints-to-alarm 3 --period 300 \
  --contact-protocols Email --notification-enabled >/dev/null

aws lightsail get-static-ip --static-ip-name "$NAME-ip" \
  --query 'staticIp.ipAddress' --output text

The matching teardown script matters just as much, since the $18 load balancer and any idle static IP keep billing until you delete them. Read it before you run it, because it removes the DNS zone too.

#!/bin/bash
set -euo pipefail
export AWS_PROFILE=${AWS_PROFILE:-lightsail-admin}
export AWS_REGION=${AWS_REGION:-us-east-1}

aws lightsail delete-load-balancer --load-balancer-name web-lb || true
for N in web-01 web-02; do
  aws lightsail detach-static-ip --static-ip-name "$N-ip" || true
  aws lightsail release-static-ip --static-ip-name "$N-ip" || true
  aws lightsail delete-alarm --alarm-name "$N-cpu-high" || true
  aws lightsail delete-instance --instance-name "$N" --force-delete-add-ons || true
done
aws lightsail get-instance-snapshots \
  --query 'instanceSnapshots[].name' --output text | tr '\t' '\n' |
  while read -r S; do aws lightsail delete-instance-snapshot --instance-snapshot-name "$S"; done
aws lightsail delete-domain --region us-east-1 --domain-name example.com || true

Your project folder should now hold five files: launch.sh, ports.json, deploy-app.sh, provision.sh, and teardown.sh. Commit them to a private Git repository. That folder is the whole infrastructure definition, and it restores your stack faster than any wiki page.

When AWS Lightsail Is the Wrong Tool

Lightsail earns its place for small, steady workloads. It stops fitting when you need autoscaling, private networking between many services, or fine-grained IAM on each resource. AWS’s own VPC peering for Lightsail lets you reach resources in a default VPC, but the service has no equivalent of EC2 auto scaling groups.

Three signals mean it is time to move. First, you add a third or fourth instance and start hand-editing load balancer targets. Second, a traffic spike forces you to resize by snapshot-and-restore during an outage. Third, compliance asks for security groups, flow logs, or private subnets you cannot configure here. When the second signal hits, containers are the natural next step, and our ECS Fargate deployment guide walks through the same kind of workload on a platform that scales on its own.

Event-driven or spiky traffic points the other way, toward no servers at all. A service that handles a few thousand requests a day costs less on functions than on an always-on $5 box. Our AWS SAM deployment tutorial covers that route.

None of this means Lightsail is on its way out. As of October 25, 2026, no official AWS result confirms that Lightsail was renamed, retired, or discontinued. AWS’s product page still invites new users to sign up, and the service reportedly picked up new regions in June and Marketplace AMI launches in August 2026. Treat it as a stable small-workload platform.

Cost Control and Teardown

Three habits keep an AWS Lightsail bill predictable. First, tag everything with the same project tag, as the commands above do, so one query finds every resource. Second, set a billing budget in AWS Billing as a backstop, since Lightsail alarms watch metrics rather than money. Third, watch the transfer allowance. The $5 plan includes 1 TB, and every gigabyte beyond a plan’s allowance starts at $0.09.

A second-order cost surprise is snapshots that outlive their instance. Deleting an instance does not delete manual snapshots, and AWS bills them at $0.05 per GB-month until you remove them. List them once a month:

aws lightsail get-instance-snapshots \
  --query 'instanceSnapshots[].[name,sizeInGb,createdAt]' --output table
aws lightsail get-static-ips --query 'staticIps[?isAttached==`false`].name' --output text
aws lightsail get-load-balancers --query 'loadBalancers[].name' --output text

The second command catches unattached static IPs, which bill $0.005 an hour. The third lists load balancers, the single biggest fixed line in this project. If you only need failover for a hobby site, a hot standby plus a manual DNS change costs $18 a month less. For long-running production, price the same capacity on EC2 using our Reserved vs Savings Plans vs Spot breakdown before deciding Lightsail is the cheap option. Its flat pricing wins at small scale, then loses once you can commit to a one- or three-year term.

Common Pitfalls

Most failed Lightsail setups trace back to six mistakes. Each one is cheap to avoid when you know it exists.

Overwriting the firewall by accident. put-instance-public-ports replaces every rule. Run it with a partial JSON file and you silently close ports you opened an hour ago. Keep ports.json as the single source of truth and always apply the whole file.

Locking yourself out of SSH. Pinning port 22 to your home address works until your ISP rotates it. Keep the lightsail-connect alias in the rule so the browser-based client still works, and keep a snapshot from before you changed anything.

Forgetting the static IP. The default public IP changes on stop and start, which breaks your A record. Attach the static IP before you create DNS entries, not after.

Running certbot with a redirect behind a load balancer. Leave the HTTPS redirect off the instances, because the load balancer handles certificates itself.

Choosing an IPv6-only bundle. The cheapest-looking bundle can be IPv6 only, and AWS says a static IP cannot attach to those instances. Check the bundle ID for ipv6 before you create anything.

Skipping swap on the smallest plan. A 0.5 GB server runs out of memory during apt upgrade or any build step. A 1 GB swap file costs nothing and prevents a whole category of mystery crashes.

Troubleshooting AWS Lightsail Setup Problems

Work through the table from the symptom column. Each fix maps to a command used earlier in the tutorial.

SymptomLikely causeFix
———
ssh: connect to host ... port 22: Connection timed outFirewall rule excludes your current IPRe-run put-instance-public-ports with a fresh checkip result, or use the browser SSH client
Permission denied (publickey)Launch script did not run, or wrong key or userUse user ubuntu, point -i at the private key, and check /var/log/cloud-init-output.log from the browser console
502 Bad Gateway from NginxApp service is down or not on the port Nginx expectssystemctl status ls-demo and journalctl -u ls-demo -n 30
Certbot says Challenge failedA record not propagated, or port 80 closedConfirm dig +short returns the static IP and the firewall allows 80
Load balancer targets show unhealthyHealth check path wrong, or instance redirects to HTTPSCheck /healthz returns 200 over plain HTTP, and remove the Certbot redirect
Load balancer certificate stuck on PENDING_VALIDATIONCNAME validation record missing in DNSAdd the exact name and value from get-load-balancer-tls-certificates
apt or pip dies with KilledOut of memory on the 512 MB planAdd the swap file from step 7, or move up one plan size
Domain not found on a domain commandCommand ran outside us-east-1Add --region us-east-1 to every *-domain* command
Static IP will not attachInstance is IPv6-only, or the IP is already attachedRecreate on a dual-stack bundle, or detach first
Restored instance has a different addressSnapshots create new instancesDetach the static IP from the old instance and attach it to the restored one
Unexpected transfer chargesTraffic past the plan’s monthly allowanceCheck NetworkOut metric, add a CDN, or move up a plan

When none of those match, ask for the launch log first. It records every command the launch script ran, and it shows a failed package install in plain text.

sudo tail -n 60 /var/log/cloud-init-output.log
sudo journalctl -u nginx -u ls-demo --since "30 min ago" --no-pager
sudo nginx -T | head -n 40

Advanced Tips for AWS Lightsail

Once the base stack runs, a handful of upgrades make it behave like production infrastructure.

Describe it as code. The CLI scripts above work, but Terraform gives you drift detection and a plan step. The AWS provider has Lightsail resources for instances, static IPs, and load balancers. Our Terraform on AWS tutorial covers the state backend and workflow that these resources need.

Put a CDN in front. A Lightsail CDN distribution lists at $2.50 a month in AWS’s own cost example, and it takes static asset traffic off your instance transfer allowance. That example prices a WordPress instance at $5, the CDN at $2.50, and an object storage bucket at $1, for $8.50 total.

Move data off the root disk. Block storage runs $0.10 per GB per month and starts at 8 GB. Mount it for uploads or databases so a snapshot restore of the instance does not roll your data back with it.

Use Marketplace images. AWS Marketplace reportedly supports launching eligible AMIs directly on Lightsail, including Ubuntu, cPanel and WHM, and Plesk. That skips the install-from-scratch step for panels and appliances.

Mix in containers. A Lightsail container service lists at $7 a month with a 500 GB monthly transfer quota. If your app already ships as a Docker image, that plan removes the patching chores you took on in step 7.

Track service changes. Lightsail bundle specs change quietly, as the $7 plan’s spec bump shows. A feed such as Cloud Releases for AWS collects AWS updates in one place, which beats rereading the pricing page every month.

Script a restore drill. Once a quarter, create an instance from the latest automatic snapshot, hit its temporary IP, and delete it. A backup you never restored is a guess.

Frequently Asked Questions

Is AWS Lightsail free?

No. The cheapest plan on AWS’s pricing page is $5 a month for a public-IPv4 Linux instance, and the FAQ still lists a $0.0047-an-hour plan capped at $3.50. Check the Lightsail console for any trial offer on your account before you pay anything.

How much does this AWS Lightsail setup cost per month?

Two $5 instances plus the $18 load balancer come to $28 a month. Add roughly $1 per 20 GB snapshot at the published $0.05 per GB-month rate. A single-instance version of the project costs about $5 plus snapshot storage.

How many static IPs can I have, and do they cost money?

AWS’s FAQ allows five per account. A static IP attached to an instance costs nothing, and an unattached one bills $0.005 an hour after the first idle hour.

Can I attach a Lightsail static IP to an IPv6-only instance?

No. AWS says static IPv4 addresses cannot attach to IPv6-only instances, so pick a dual-stack bundle when you need a fixed public IPv4 address.

Do Lightsail snapshots cost extra?

Turning on automatic snapshots is free, and you pay for the storage they use. AWS lists snapshots at $0.05 per GB-month and keeps seven automatic ones, according to its FAQ. Delete manual snapshots you no longer need.

Should I use Lightsail or EC2?

Use Lightsail for a few servers with predictable traffic and a fixed monthly bill. Use EC2 when you need autoscaling, custom VPC design, per-resource IAM, or reserved-capacity discounts.

Does the Lightsail load balancer support HTTPS?

Yes. Lightsail load balancers include free managed TLS certificates, and AWS handles renewals. You prove domain ownership with a DNS CNAME, then attach the certificate and switch on the HTTP to HTTPS redirect.

Is AWS Lightsail being discontinued?

No official AWS result as of October 25, 2026 says so. The service is listed as available, and no retirement or discontinuation is indicated.

Sofia Lindström

Sofia Lindström

Editor-in-Chief

Sofia Lindström is the Editor-in-Chief at Tech Insider, where she leads editorial strategy and oversees coverage across AI, cybersecurity, and enterprise technology. With over a decade in Swedish tech journalism, she previously served as technology editor at Dagens Industri and covered the Nordic startup ecosystem for Breakit. Sofia holds an MSc in Media Technology from KTH Royal Institute of Technology and is a frequent speaker at Web Summit and Slush. She is passionate about making complex technology accessible to business leaders.

View all articles