Template Catalog
L1ScriptedBeginner

EC2 + Docker Bootstrap Script

Provision the server once, the same way, every time.

Overview

Same EC2 box — but instead of typing commands by hand, you hand AWS a bootstrap script (user-data) that installs Docker, Git, Nginx, and Certbot, then launches your app on first boot. This is your first taste of repeatability: spin up an identical server in one click.

Architecture

Your laptop
user-data script
Nginx (reverse proxy + TLS)
Docker container
Certbot / Let's Encrypt

EC2 runs a user-data script on first boot that installs the runtime, starts your container, and configures Nginx as a TLS-terminating reverse proxy in front of it.

  1. 1You paste the bootstrap script into the instance's user-data field.
  2. 2On first boot, the script installs Docker, Nginx, and Certbot.
  3. 3The script starts your container on an internal port (e.g. 8080).
  4. 4Nginx terminates HTTPS on 443 and proxies to the container.

An EC2 instance boots and runs a user-data script; Nginx terminates TLS on port 443 and proxies to a Docker container on an internal port.

What you'll understand

  • Use EC2 user-data to run a setup script automatically on first boot.
  • Write an idempotent bootstrap script that installs a full runtime.
  • Put Nginx in front of your container as a reverse proxy.
  • Add free HTTPS with Certbot and Let's Encrypt.

Prerequisites

Generated files

The files this template produces. Copy any of them straight into your project.

2 files

Runs automatically on first boot to provision the whole server.

user-data.sh
bash
#!/usr/bin/env bash
set -euo pipefail

# --- Install runtime ---
dnf update -y
dnf install -y docker git nginx
systemctl enable --now docker nginx

# --- Run the app container on an internal port ---
docker run -d --name web \
  -p 127.0.0.1:8080:80 \
  --restart unless-stopped \
  nginx:1.27-alpine

# --- Reverse proxy: Nginx -> container ---
cat >/etc/nginx/conf.d/app.conf <<'NGINX'
server {
  listen 80;
  server_name _;
  location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}
NGINX
nginx -t && systemctl reload nginx

echo "Bootstrap complete: $(date -u)" >/var/log/bootstrap-done.log

Step-by-step guide

  1. 1

    Write the bootstrap script

    Take the generated user-data.sh. It's idempotent-ish: every fresh instance ends up in the same known state. Read it top to bottom so nothing is magic.

  2. 2

    Launch with user-data

    Launch a new t3.micro and paste the script into Advanced details → User data. AWS runs it as root on first boot.

    User-data only runs on the FIRST boot by default — reboots won't re-run it.

  3. 3

    Verify it provisioned itself

    SSH in and confirm Docker, the container, and Nginx all came up without you touching them.

    sudo cat /var/log/cloud-init-output.log | tail -n 40

    Shows what the user-data script actually did on boot.

    docker ps && systemctl status nginx --no-pager
  4. 4

    Add HTTPS

    Point a domain's A record at the public IP, then run the Certbot helper to get a free, auto-renewing TLS certificate.

    sudo bash enable-tls.sh app.example.com you@example.com

    Issues a cert and rewrites Nginx to redirect HTTP→HTTPS.

AI insight

Ask the assistant to explain, review, or recommend — authored for this template.

AI insightAuthored

What changed from Level 0

The server is the same, but you no longer touch it by hand. A user-data script runs once on first boot to install everything and start the app, and Nginx now sits in front of your container to terminate TLS and proxy requests in.

  • user-data = a script AWS runs as root on first boot.
  • Nginx = reverse proxy + HTTPS termination on 443.
  • Certbot = free, auto-renewing Let's Encrypt certificates.

Security notes

  • TLS is now handled

    Info

    Certbot gives you real HTTPS with automatic renewal — a genuine step up from Level 0.

  • User-data runs as root

    Medium

    Anything in the script runs with full privileges, so a mistake or secret in it is dangerous.

    Keep secrets out of user-data; later levels inject them via the pipeline.

  • Still a one-off server

    Low

    The script is repeatable, but networking, IAM, and the instance itself are still created by hand.

    Level 2 captures all of that in Terraform.

Cost notes

Free tier~$7.50/mo~$0.01/hr if left running · free-tier eligible
  • EC2 t3.micro + public IPv4

    Same free-tier footprint as Level 0 (~$7.50 + ~$3.60/month if left running).

  • Certbot / Let's Encrypt

    Free. No cost for issuing or renewing certificates.

Cleanup guide

Tear it down when you're done — the fastest way to avoid a surprise bill.

  1. 1

    Terminate the EC2 instance.

    Billing
  2. 2

    Remove the DNS A record you created for TLS.

  3. 3

    Delete the key pair and security group if unused.

Troubleshooting

Where to go next