Lightsail is AWS's simple corner. A fixed monthly price, a single button to create a Linux box, ssh in, run your code. None of the EC2 add-on charges, none of the IAM ceremony. The honest case for being on AWS without paying the AWS-complexity tax.
Most people who want a small Linux server to run a Node API don't actually need AWS. DigitalOcean droplets, Hetzner Cloud, Fly machines β all simpler, all cheaper, all faster to set up. The reason to pick Lightsail anyway is one of two things: you already have AWS infrastructure (S3 buckets, RDS databases, SES email) that this server needs to talk to and you want the integration; or your company has an "AWS only" policy. Outside those, Lightsail is fine but not winning on merits.
The thing Lightsail gets right β the reason it exists β is that EC2 is genuinely too complicated for "I want a $5 Linux box." EC2's pricing has half a dozen line items per instance; Lightsail charges one. EC2 hides the public IP behind an Elastic IP that you can attach for free until you detach it, at which point you pay for it; Lightsail's static IP is included. EC2 has security groups, network ACLs, NACL associations, VPCs, subnets, and route tables; Lightsail has a firewall toggle. The trade-off is that Lightsail has fewer knobs β if your workload needs them, you grow out of Lightsail and move to EC2.
This tutorial walks creating a Lightsail instance with the Node.js blueprint, deploying your app, running it with a process manager, opening the firewall, attaching a domain, and getting a free TLS cert. End state: https://api.mybrand.com resolving to your Node API. We'll use the AWS console for the create-instance flow (the CLI works but the console is genuinely shorter here) and the shell for everything after.
In the AWS console, open Lightsail (note: it has its own console at lightsail.aws.amazon.com, separate from the main AWS console β works the same, different URL). Click Create instance:
us-east-1 is fine if you're unsure.mybrand-api-prod.Click Create. The instance boots in 60β90 seconds. Lightsail assigns it a public IP and an internal name. The first month is free under the AWS Free Tier for new accounts.
Two ways:
~/.ssh/lightsail.pem, chmod 600 ~/.ssh/lightsail.pem, then:
ssh -i ~/.ssh/lightsail.pem bitnami@<PUBLIC_IP>
# Username is bitnami for the Node.js blueprint, ubuntu for the Ubuntu blueprint.
You're in. The Node.js blueprint puts you in a home directory with /opt/bitnami/ holding nginx and the pre-baked Node sample app.
# Option A: git clone (public repo or deploy key)
cd ~
git clone https://github.com/you/your-api.git
cd your-api
npm ci --production
# Set env vars in /etc/environment or a .env file your app reads.
# Option B: rsync from your laptop (no git access on the server)
# From your laptop:
rsync -avz --exclude node_modules \
-e "ssh -i ~/.ssh/lightsail.pem" \
./ bitnami@<PUBLIC_IP>:/home/bitnami/your-api/
# Then on the server:
cd ~/your-api
npm ci --production
Test it manually first: node server.js (or your entry). Make sure it boots and listens on a port β typically 3000 or 8080. Stop it (Ctrl+C); we'll run it persistently in the next step.
PM2 is pre-installed on the Node.js blueprint. On a plain Ubuntu blueprint: sudo npm i -g pm2.
cd ~/your-api
pm2 start server.js --name api
pm2 save
pm2 startup
# pm2 startup prints a command β run it as root to register PM2
# as a systemd service so it survives reboots.
Now your API is running on http://<PUBLIC_IP>:3000 (or whatever port your app listens on). Test:
curl http://<PUBLIC_IP>:3000/health
# Currently this will time out β the firewall blocks it. That's Step 5.
Useful PM2 commands: pm2 logs api (live logs), pm2 restart api, pm2 monit (interactive dashboard).
Lightsail's firewall blocks everything by default except SSH (22). Open the web ports:
Don't open port 3000 directly to the public β nginx will sit in front of your Node app on 80/443 (the blueprint already has nginx running) and proxy requests internally to your Node process. Run curl http://<PUBLIC_IP> from your laptop; you should hit the default nginx page or the blueprint's sample app.
On the Node.js blueprint, nginx config lives at /opt/bitnami/nginx/conf/server_blocks/. Create a file there:
sudo nano /opt/bitnami/nginx/conf/server_blocks/api.conf
Paste:
server {
listen 80;
server_name api.mybrand.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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;
}
}
Reload: sudo /opt/bitnami/ctlscript.sh restart nginx. nginx is now proxying api.mybrand.com:80 to your Node process on 127.0.0.1:3000.
Lightsail's default IP is "dynamic" β survives reboots but not power-cycles. For a real domain you want a static IP that doesn't change:
Now point your domain at the static IP. In Route 53 (or your DNS provider): create an A record with name api and value = your static IP. DNS tutorial covers the records in general; this is a straight A-record case.
On the Node.js blueprint, certbot is pre-installed. SSH in and run:
sudo /opt/bitnami/bncert-tool
The tool asks for your domain name, your email, and whether you want HTTP-to-HTTPS redirect. Say yes to redirect. It modifies the nginx config, issues the cert, sets up auto-renewal. Two minutes, no manual nginx editing.
On a plain Ubuntu blueprint without Bitnami: sudo apt install certbot python3-certbot-nginx, then sudo certbot --nginx -d api.mybrand.com --redirect. Same result, slightly more steps.
Test: curl -I https://api.mybrand.com/health. You should see HTTP/2 200 (or whatever your health endpoint returns). HTTPβHTTPS redirect is in place; cert auto-renews every 90 days via a cron job.
For pushes-deploy code updates, the steady-state workflow is:
# Local: push to your repo
git push origin main
# Server: pull + restart
ssh -i ~/.ssh/lightsail.pem bitnami@<STATIC_IP>
cd ~/your-api
git pull
npm ci --production # if dependencies changed
pm2 restart api
For something snappier, write a shell script on your laptop that runs the same commands over ssh:
#!/usr/bin/env bash
ssh -i ~/.ssh/lightsail.pem bitnami@<STATIC_IP> \
'cd ~/your-api && git pull && npm ci --production && pm2 restart api'
For real CI/CD, a GitHub Actions workflow that ssh's in and runs the same is ~15 lines. For zero-downtime deploys (a meaningful step up), look at PM2's reload command or pair PM2 with a small reverse-proxy switch.
You need autoscaling. Lightsail is single-instance. If traffic outgrows one box, you need EC2 + ALB + Auto Scaling Group, or move to a container orchestrator like ECS Fargate.
You need VPC peering or private subnets. Lightsail instances live in a managed VPC; you can't peer to other VPCs. If the server needs to talk to RDS / ElastiCache / private resources on the AWS side, EC2 is the path.
You need more than the $80/month tier (32 GB RAM, 8 vCPU). Lightsail's largest size is fixed; beyond that, EC2 is the only option.