Report this

What is the reason for this report?

Using cloud-init to automate the Let's Encrypt process on new Ubuntu/nginx droplets

Posted on July 27, 2017
Andy Hattemer

By Andy Hattemer

Community Builder

Assuming you already have a top-level domain setup on DigitalOcean (e.g.: mysite.com) the script below will (for Ubuntu Droplets):

  1. Install nginx and Let’s Encrypt
  2. Create a DigitalOcean Floating IP pointing at the droplet.
  3. Update your top-level domain’s DNS record to point a new subdomain at the droplet, based on the droplet name.
  4. Use Let’s Encrypt (certbot) to generate SSL certs for the domain
  5. Setup a cron job to renew certs periodically.

Using cloud-config.

So if you paste this in to the User-Data section when creating a droplet named mydroplet and you have a domain mysite.com, you’ll end up with a properly configured https://mydroplet.mysite.com that gets an A on the SSLabs test.

The only thing you need to change in the script below is to set the DO_API_TOKEN env variable to your token, and DOMAIN to the top-level domain you already have setup in the DO control panel.

#cloud-config
packages:
  - nginx
  #jq is a command-line json processor https://stedolan.github.io/jq/
  - jq
  - unattended-upgrades
runcmd:
  - export DOMAIN=your_domain_here.com
  - export DO_API_TOKEN=PASTE_YOUR_DIGITALOCEAN_API_TOKEN_HERE
  - export PUBLIC_IPV4=$(curl -s http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address)
  - export DROPLET_ID=$(curl -s http://169.254.169.254/metadata/v1/id)
  - export DROPLET_NAME=$(curl -s http://169.254.169.254/metadata/v1/hostname)
  # get the email for letsencrypt from do api
  - 'export EMAIL=$(curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $DO_API_TOKEN"  https://api.digitalocean.com/v2/account  | jq -r ".account.email")'
  # install certbot, update
  - add-apt-repository ppa:certbot/certbot -y
  - apt-get update
  - apt install python-certbot-nginx -y
  # add domain name to nginx config, restart it
  - sed -i 's/server_name _;/server_name '$DROPLET_NAME"."$DOMAIN';/' /etc/nginx/sites-available/default
  - systemctl restart nginx
  # create a floating ip
  - 'export FLOATING_IP=$(curl -X POST -H ''Content-Type: application/json'' -d ''{"droplet_id":"''"$DROPLET_ID"''"}'' -H "Authorization: Bearer $DO_API_TOKEN"  https://api.digitalocean.com/v2/floating_ips  | jq -r ".floating_ip.ip")'
  # create a subdomain a-record for this droplet
  - 'curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $DO_API_TOKEN" -d "{\"type\":\"A\", \"name\":\"$DROPLET_NAME\", \"data\":\"$FLOATING_IP\"}" https://api.digitalocean.com/v2/domains/$DOMAIN/records'
  - sleep 30s
  - certbot --nginx -n -d $DROPLET_NAME"."$DOMAIN --email $EMAIL --agree-tos --redirect --hsts
  - systemctl reboot
# add renewal cron
write_files:
  - owner: root:root
    path: /etc/cron.d/letsencrypt_renew
    content: "15 3 * * * /usr/bin/certbot renew --quiet"


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

These answers are provided by our Community. If you find them useful, show some love by clicking the heart. If you run into issues leave a comment, or add your own answer to help others.

Hey @aha,

What you’ve created is quite GREAT!

You’ll need to update the IP in the Droplet_ID, PUBLIC_IPV4, and DROPLET_NAME but other than that, all is good. I’ve actually tried it and all is good.

Anyway, if you want to make any optimizations,

  1. Use the DigitalOcean API to get the Droplet ID - instead of relying on the Droplet metadata service to get the Droplet ID, use the DigitalOcean API to retrieve it. This can be done with a simple API call, which can help make the script more reliable and less dependent on the metadata service.

  2. Remove unnecessary dependencies - the script installs the jq command-line tool, which is used to parse JSON output. While it can be useful, it’s not strictly necessary for this script. Removing this dependency can help make the script smaller and faster.

  3. Add error handling - the script assumes that all commands will execute successfully. However, there are many potential failure points (e.g., network connectivity issues, DigitalOcean API errors, etc.) that could cause the script to fail. Adding some basic error handling (e.g., checking exit codes and logging errors) can help make the script more robust and easier to troubleshoot.

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.