Related blog post.
This is a demo repo for provisioning a single server using Terraform on Digital Ocean.
This is primary designed (but not limited) to run app servers that are deployed using Kamal (hence user created named kamal
).
After provisioning a server, you can use Kamal with this demo repo that shows how to deploy an app that runs a node.js/bun frontend + backend + database stack
- Generate two pairs of SSH keys, one for the root user and one for the kamal user (something like `ssh-keygen -t ed25519 -C "")
- Generate a new API key in Digital Ocean web console
- Upload the root user public key to the Digital Ocean web console and replace
"terraform do"
incloud.tf
with it - Create a file
terraform.tfvars
at the root of the repository with your Digital Ocean API key and SSH key to be used for kamal, it will look like this:There are additional variables you can set; but they have defaults. Checkhost_api_key = "your-digital-ocean-api-key" ssh_vps_kamal_key = "<your-ssh-kamal-public-key>"
variables.tf
. - Run
terraform init
to initialize the terraform environment - Run
terraform plan
to see the changes that will be applied - Run
terraform apply
(and when prompted, typeyes
and hit enter) to apply the changes - When this is done, the new droplet should appear in the Digital Ocean web console. You should also see a firewall in the web console
- Copy the IP address of the droplet
- Run
ssh root@<ip address>
. You might need to retry (a few times) after a few seconds if it doesn't connect yet - Run
echo $SHELL
and it is probably/bin/bash
. If so, wait at the prompt until the machine says it's rebooting. This will happen after a few minutes, but unlikely to take more than 10 minutes. - Run step 10-11 again and this time,
echo $SHELL
should say/bin/fish
As you tweak the configuration, it'll be useful to do:
- Run
terraform apply
- Run
terraform destroy
- Tweak configuration files
- Run
rm *.tfstate *.tfstate.backup; terraform apply
- Repeat 2-5
Depending on what you do (usually because you didn't do terraform destroy
and manually deleted the droplets in the web console), sometimes you might find that the firewall objects aren't destroyed and rm *.tfstate *.tfstate.backup; terraform apply
would then fail.
You can import the existing firewall object:
- With your API key, run
curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer <api key>" "https://api.digitalocean.com/v2/firewalls"
and look up theid
for the fire wall and then: - Run
terraform import digitalocean_firewall.vps <firewall id>
terraform apply
(without deleting the*.tfstate
files)
This script is based on the great work of dylanjcastillo in terraform-kamal-single-vps which is in turn based on luizkowalski in terraform-hetzner.