NixOS + droplet = "nixlet" π¦
a terraform module to create a nixos droplet on digitalocean. heavily inspired by elitak/nixos-infect but tuned explicitly for our tools, processes, and preferences. the goal is a quick to setup (ephemeral or long-lasting!) nixos host on digitalocean without too much headache and to provide an easy way for new developers to begin experimenting with this operating-system/platform.
- ποΈ no nix installation necessary! π only terraform and a
DIGITALOCEAN_TOKEN! - β²οΈ deployed and ready in "under 5-microwave minutes" π.
- π·ββοΈ custom configuration is easy! π¬ (but also not required) π
- π‘ advanced droplet monitoring enabled! π½ πΈ
- π¦Ί demonstrations.
- π a safe and productive learning environment for beginners to explore.
- π developer's scratchpad.
- π¬ "clean room" for further analysis.
- π¦ ephemeral services
- tailscale node(s).
- dnscrypt-proxy2 encrypted DNS server.
- caddy web server.
- mastodon federated social network server.
- logging/tracing/event processing.
- code-server.
- github-runner/gitlab-runner
- grafana, prometheus, victoriametrics.
- factorio server
- minio s3 filestore
- jupyterhub with support for any kernel you want badly enough.
- so many more...
Configuration can live next to .tf files, by using file():
module "nixlet" {
# note that every other value (besides source) is NOT required.
source = "github.com/polis-dev/nixlet.tf"
# increased droplet size to make nixlet go vroooom!
droplet_size = "s-4vcpu-8gb-intel"
# defaults to nixos-unstable, with flakes, and other sane defaults.
nixos_channel = "nixos-unstable"
# custom nixos configuration can be specified via file() like so.
nixos_config = file("${path.module}/custom.nix")
}OR configurations can embed/inline the nix file to leverage terraform's string interpolation:
locals { domain = "example.com" }
module "nixlet" {
# note that every other value (besides source) is NOT required.
source = "github.com/polis-dev/nixlet.tf"
nixos_config = <<-NIXOS_CONFIG
{ config, lib, pkgs, ... }: {
networking.domain = "${local.domain}";
/*
add your configuration here...
*/
}
NIXOS_CONFIG
}Then run terraform init and then terraform plan.
| Name | Version |
|---|---|
| cloudinit | ~> 2.3 |
| digitalocean | ~> 2.32 |
| Name | Type |
|---|---|
| digitalocean_droplet.main | resource |
| digitalocean_floating_ip.main | resource |
| cloudinit_config.user_data | data source |
| digitalocean_ssh_keys.all | data source |
| digitalocean_vpc.main | data source |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| backups | enable regular digitalocean droplet backups | bool |
false |
no |
| droplet_size | which digitalocean droplet size should be used? | string |
"s-1vcpu-1gb-intel" |
no |
| droplet_tags | tags to apply to droplet. | list(string) |
[] |
no |
| flake_config | file contents of flake.nix (if empty, default will be generated) | string |
"" |
no |
| floating_ip | reserve a floating IP droplet host? | bool |
true |
no |
| graceful_shutdown | allow this droplet to shutdown gracefully? | bool |
false |
no |
| hostname | what name should be given to instance? | string |
"nixlet" |
no |
| image | change this at your own risk. it "just works" like this... | string |
"debian-11-x64" |
no |
| infect_script | file contents of infect.sh (if empty, default will be used) | string |
"" |
no |
| ipv6 | enable ipv6? | bool |
true |
no |
| nixos_channel | which nix channel should be used for managed hosts? | string |
"nixos-unstable" |
no |
| nixos_config | file contents of custom.nix (if empty, default will be used) | string |
"" |
no |
| nixos_system | n/a | string |
"x86_64-linux" |
no |
| region | which digitalocean region should be used? | string |
"nyc3" |
no |
| resize_disk | resize disk when resizing the droplet (permanent change) | bool |
false |
no |
| ssh_key_ids | ssh key ids to grant root ssh access. does not create them. if unspecified, all currently available ssh keys will be used (within the project containing this API token). | list(number) |
[] |
no |
| volume_ids | list of volumes to be mounted to the created droplet. | list(number) |
[] |
no |
| vpc_name | name of the VPC to target, if "default" will be appended with -$region | string |
"default" |
no |
| Name | Description |
|---|---|
| droplet | (augmented) droplet resource |
| floating_ip | (augmented) floating_ip resource |
| ipv4_address | public ipv4 address |
| ipv6_address | public ipv6 address |
-
install terraform and terraform-docs.
-
clone the repository and run
./terraform.sh initto setup providers. you can run./terraform.shto see available commands. -
export
DIGITALOCEAN_TOKEN=...(directly, or via the newly created.envrc) to set your access credentials. -
plan a deployment with
./terraform.sh plan. -
apply the plan with
./terraform.sh apply. allow ~5 minutes (with default config) to provision completely (or justtail -f /var/log/cloud-init-output.log). -
ssh to your host and enjoy! clean up everything with
./terraform.sh destroy.
"i logged into my host and its not nixos! its debian!":
well, "future self at 3am", you can read through the log output from cloud-init with less /var/log/cloud-init-output.log. often a small syntax error or a minor typo can cause the initial build to fail. you will almost certainly want to start the provisioning process over entirely after making your correction locally; luckily with terraform thats pretty easy: ./terraform.sh apply -destroy, then ./terraform.sh apply to create it again.
NOTE that the documentation is automatically updated by terraform-docs.