doctl for command line deployment of digitalocean droplet
Feb 13, 2025
Introduction
In this post you will find my notes on deploying a digitalocean
droplet, the name digitalocean gives to a virtual private server (VPS)
instance, from the command line. For context, this website runs on a
digitalocen droplet, running nginx. My goal is to use doctl
,
the command line tool provided by digitalocean, to make this process quick
and reproducible.
The reference for this post is located in the digitalocean docs: Set up a Production-Ready Droplet . You also have to choose the Using the doctl CLI tab in the "Before You Start" section to follow along. This post documents my process following the documentation. My goal is to document my results here, while trying things out and making adjustments.
warning
A reminder that launching a droplet on digitalocean is NOT free. So, proceed with caution and make sure you have the funds available to move forward :)
Prerequisites
Install doctl
The first step is to install doctl
following the instructions
here
. I used the "GitHub Download (Linux, macOS)" instructions, as
detailed below.
First up, get the gzipped archive (check for the latest version at the link above):
$ cd ~
$ wget https://github.com/digitalocean/doctl/releases/download/v1.120.1/doctl-1.120.1-linux-amd64.tar.gz
Next, extract the binary and move it to /usr/local/bin/
$ tar xf ~/doctl-1.120.1-linux-amd64.tar.gz
$ sudo mv ~/doctl /usr/local/bin
Authenticate doctl and set CONTEXT
In order to authenticate doctl
you need to create a token
at the digitalocean website, as detailed
here
. Of course, this means that you need a digitalocean account
to continue. Be sure to record the token in a safe
place!
Finally, you need to authenticate and give the context a name. In this example
I'll use my_account (you will be prompted for the token here):
$ doctl auth init --context my_account
and switch to the created context using
$ doctl auth switch --context my_account
Validate and check account
Everything should be set to go at this point. A final check will pull your digitalocean account details as a check:
$ doctl account get
SSH keys
An ssh key needs to be added to digitalocean for secure access to created resources, like the droplet we want to spin up. There are two ways to do this:
- Using doctl (be sure to scroll up and click on the "Using the doctl CLI" tab to get command line information), or
- Using the digitalocean control panel (web interface) .
I will create a new key, just for digitalocean testing:
$ ssh-keygen -f ~/.ssh/do_test
This will prompt for a passphrase. If you want no passphrase, you can hit enter twice. Of course, if you choose a passphrase store it carefully for the next time you use this ssh key. We can check that the key file was created using
$ ls -la ~/.ssh/do_*
-rw------- 1 username username 2610 Feb 19 15:02 /home/username/.ssh/do_test
-rw-r--r-- 1 username username 575 Feb 19 15:02 /home/username/.ssh/do_test.pub
Next, we have to import this key into digitalocean, giving in the name
do_test
$ doctl compute ssh-key import do_test --public-key-file ~/.ssh/do_test.pub
ID Name FingerPrint
[id num] do_test [ --- fingerprint for ssh key --- ]
Finally, list all the ssh keys that digitalocean knows using
$ doctl compute ssh-key list
This listing shows all keys, including the FingerPrint, that will be needed below.
How to create a droplet
Now that the prerequisites are out of the way we can spin-up a droplet with a "simple" command. Seriously, there are still a variety of things to specify, but we're close to the end. A bash script can be included to accomplish a bunch of things that are important when we create a new droplet/VPS:
- create a new, non-root user,
- NOTE: this user will be added to the sudo group and
will be required to set a password the first time that you ssh to the
droplet. This way
sudo
can still be used on the droplet with password required-- the usual :)
- NOTE: this user will be added to the sudo group and
will be required to set a password the first time that you ssh to the
droplet. This way
- copy over ssh keys and set permissions/ownership, and
- disable root ssh login
Following
here
,
the contents of the script ( call it my-script.sh
) should be
something like this (be sure to change the username):
#!/bin/bash
set -euo pipefail
# CHANGE USERNAME
# -- this will be a sudo, not-root user
USERNAME=my_username
# - create user
# - immediately expire password to force a password change on login
useradd --create-home --shell "/bin/bash" --groups sudo "${USERNAME}"
passwd --delete "${USERNAME}"
chage --lastday 0 "${USERNAME}"
# Create SSH directory for sudo user and move keys over
home_directory="$(eval echo ~${USERNAME})"
mkdir --parents "${home_directory}/.ssh"
cp /root/.ssh/authorized_keys "${home_directory}/.ssh"
chmod 0700 "${home_directory}/.ssh"
chmod 0600 "${home_directory}/.ssh/authorized_keys"
chown --recursive "${USERNAME}":"${USERNAME}" "${home_directory}/.ssh"
# Disable root SSH login with password
sed --in-place 's/^PermitRootLogin.*/PermitRootLogin prohibit-password/g' /etc/ssh/sshd_config
if sshd -t -q; then systemctl restart sshd; fi
The actual creation of the droplet, using the script above, would have a command like this:
doctl compute droplet create MY-DROPLET \
--tag-names MY-TAG \
--image MY-IMAGE \
--region MY-REGION \
--size MY-SIZE \
--ssh-keys MY-SSH-FINGERPRINT \
--user-data-file my-script.sh \
--enable-ipv6 --enable-monitoring \
--enable-private-networking --enable-backups
where the following options can be set
- MY-DROPLET: the name of the droplet/VPS
- MY-TAG: a tag for the droplet/VPS
- MY-IMAGE: the image used for the droplet/VPS. Options can be seen using
$ doctl compute image list --format="ID, Slug, Name"
ID Slug Name
112379295 convoy Convoy 0.6.0 on Ubuntu 20.04
113137715 nakama-18-04 Nakama 3.12.0 on Ubuntu 18.04
114133748 bigcloud-odoo Odoo 14 on Ubuntu 20.04
114445163 rstudio-20-04 RStudio 2021.09.2+382 on Ubuntu 20.04
114537960 invoiceninja-20-0-4 Invoice Ninja 5 on Ubuntu 20.0.4
116403190 appsmith-18-04 Appsmith 1.7 on Ubuntu 20.04
116470971 npool nPool 1.8 on Ubuntu 20.04
117726186 botguardo-botguardingressc BotGuard Ingress Controller 1.0 on Debian 11 (bullseye)
117873390 intel-intelgprofilercr Intel gProfiler Crypto Demo 1.3 on Ubuntu 20.04
--- cut-off --- - MY-REGION: the geographical location of the droplet. Options can be seen
using
$ doctl compute region list
Slug Name Available
nyc1 New York 1 true
sfo1 San Francisco 1 false
nyc2 New York 2 true
ams2 Amsterdam 2 false
sgp1 Singapore 1 true
lon1 London 1 true
nyc3 New York 3 true
ams3 Amsterdam 3 true
fra1 Frankfurt 1 true
tor1 Toronto 1 true
sfo2 San Francisco 2 true
blr1 Bangalore 1 true
sfo3 San Francisco 3 true
syd1 Sydney 1 true - MY-SIZE: the size the droplet/VPS. Options can be seen using
$ doctl compute size list --format="Slug, Description, Price Monthly, Price Hourly"
Slug Description Price Monthly Price Hourly
s-1vcpu-512mb-10gb Basic 4.00 0.005950
s-1vcpu-1gb Basic 6.00 0.008930
512mb Legacy Basic 6.00 0.008930
s-1vcpu-1gb-amd Basic AMD 7.00 0.010420
s-1vcpu-1gb-intel Basic Intel 7.00 0.010420
s-1vcpu-1gb-35gb-intel Basic Intel 8.00 0.011900
s-1vcpu-2gb Basic 12.00 0.017860
1gb Legacy Basic 12.00 0.017860
s-1vcpu-2gb-amd Basic AMD 14.00 0.020830
--- cut-off --- - MY-SSH-FINGERPRINT: the fingerprint for the ssh key added above. This
information is available using
$ doctl compute ssh-key list
- The last four flags are pretty self-explanatory, but more information
can be found
here
. Almost certainly you want
--enable-ipv6
and--enable-private-networking
,
--enable-monitoring
and--enable-backups
.
A concrete example
Okay, let's try this out using the information above. The script I will use
is secure-vps.sh
containing:
#!/bin/bash
set -euo pipefail
# CHANGE USERNAME -- this will be sudo, not-root user
USERNAME=testuser
# - create user
# - immediately expire password to force a password change on login
useradd --create-home --shell "/bin/bash" --groups sudo "${USERNAME}"
passwd --delete "${USERNAME}"
chage --lastday 0 "${USERNAME}"
# Create SSH directory for sudo user and move keys over
home_directory="$(eval echo ~${USERNAME})"
mkdir --parents "${home_directory}/.ssh"
cp /root/.ssh/authorized_keys "${home_directory}/.ssh"
chmod 0700 "${home_directory}/.ssh"
chmod 0600 "${home_directory}/.ssh/authorized_keys"
chown --recursive "${USERNAME}":"${USERNAME}" "${home_directory}/.ssh"
# Disable root SSH login with password
sed --in-place 's/^PermitRootLogin.*/PermitRootLogin prohibit-password/g' /etc/ssh/sshd_config
if sshd -t -q; then systemctl restart sshd; fi
and the command given is (this spins up an Ubuntu image with nginx):
doctl compute droplet create test-droplet \
--tag-names test-tag \
--image nginx \
--region nyc1 \
--size s-1vcpu-1gb \
--ssh-keys [CENSORED] \
--user-data-file secure-vps.sh \
--enable-ipv6 \
--enable-private-networking
I put the above command into a bash script and ran the script, outputing:
$ ./create-droplet.sh
ID Name Public IPv4 Private IPv4 Public IPv6 Memory VCPUs Disk Region Image VPC UUID Status Tags Features Volumes
477700144 test-droplet 1024 1 25 nyc1 Ubuntu NGINX 1.23.3 on Ubuntu 22.04 new test-tag droplet_agent
A list of droplets on my account, seconds later showed (IPs are censored for obvious reasons):
$ doctl compute droplet list
ID Name Public IPv4 Private IPv4 Public IPv6 Memory VCPUs Disk Region Image VPC UUID Status Tags Features Volumes
477700144 test-droplet [censored] [censored] [censored] 1024 1 25 nyc1 Ubuntu NGINX 1.23.3 on Ubuntu 22.04 [censored] active test-tag droplet_agent,private_networking,ipv6
Opening a browser and entering the "Public IPv4" for the url brought up the standard "Welcome to nginx!" page. Nice.
ssh to droplet
Next, we ssh to the droplet. A first step is to add this identity to my
~/.ssh/config
file so that ssh knows the correct identity and
key to use. I add the following entry
Host do_test
HostName [censored Public IPv4 for droplet]
IdentityFile ~/.ssh/do_test
User testuser
With this entry, ssh to the droplet should be as simple as
$ ssh do_test
As noted above, the password for "testuser" will have to be set the first time
that you ssh to the droplet-- the script sets it up so this is required. Be
sure to create a strong password and save it carefully
for use whenever sudo
is needed on the droplet.
delete the droplet
After checking out the droplet as much as you'd like it is time to delete the droplet so that the charges don't add up. This can be done with (use left/right arrows to select "yes" when prompted):
$ doctl compute droplet delete test-droplet
❯ Are you sure you want to delete this Droplet? yes
You can check that test-droplet
is not listed among the active
droplet using
$ doctl compute droplet list
Well, that's it for this post. I've tried it all out as of Feb 20, 2025 and everything worked great!