Terraform-ing Jitsi Meet into a Digital Ocean Droplet

I’ve been trying to stand up my own instance(s) of Jitsi Meet, but found it very painful to manually stand up the infrasructure.

The prework

After doing all the steps I mentioned here I stopped the containers and took a snapshot, so I could avoid having to redo all the configuration.

I also set up DNS records (managed outside of Digital Ocean) to point at a fixed IP address (“Floating IP”, inside Digital Ocean); this IP address can then be attached to any instance I spin up.

The manual work

Once I’d done this, I still had to do all the following by hand:

  1. Create the droplet from a snapshot
  2. Attach the droplet to the firewall(s)
  3. Attach the floating IP address to the droplet
  4. Log in to the instance & run docker-compose up -d

Automating it!

This was slow, repetative, and boring, so I had a go at making (via lots searching/reading documentation/copy-pasting/trial&error) a Terraform script to do it for me; its below!

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
# Use -var="do_token=..." CLI option to pass the token in
# or wait for the prompt
variable "do_token" {}

# Configure the DigitalOcean Provider
provider "digitalocean" {
  token = var.do_token
}


# Locate the Snapshot
data "digitalocean_droplet_snapshot" "configuredJitsiSnapshot" {
  name_regex  = "^jitsi-configured-2"
  region      = "lon1"
  most_recent = true
}


# Create the instance from the snapshot
resource "digitalocean_droplet" "jitsiDroplet" {
  name          = "jitsiMeet"
  size          = "s-4vcpu-8gb"
  image         = data.digitalocean_droplet_snapshot.configuredJitsiSnapshot.id
  region        = "lon1"
  monitoring    = true
  user_data     = <<-EOF
		#! /bin/bash
        cd  /home/jitsi/docker-jitsi-meet/
        docker-compose up -d
	EOF
}

# Attach firewalls to the instance
resource "digitalocean_firewall" "inbound_rules" {
  name = "jitsi-reqs-in"

  droplet_ids = ["${digitalocean_droplet.jitsiDroplet.id}"]
  inbound_rule {
      protocol           = "tcp"
      port_range         = "80"
      source_addresses   = ["0.0.0.0/0", "::/0"]
    }
    inbound_rule {
      protocol           = "tcp"
      port_range         = "443"
      source_addresses   = ["0.0.0.0/0", "::/0"]
    }
    inbound_rule {
      protocol           = "tcp"
      port_range         = "4443"
      source_addresses   = ["0.0.0.0/0", "::/0"]
    }
    inbound_rule {
      protocol           = "udp"
      port_range         = "10000"
      source_addresses   = ["0.0.0.0/0", "::/0"]
    }
}
resource "digitalocean_firewall" "outbound_rules" {
  name = "jitsi-reqs-out"

  droplet_ids = ["${digitalocean_droplet.jitsiDroplet.id}"]

  outbound_rule  {
      protocol           = "tcp"
      port_range         = "53"
      destination_addresses   = ["0.0.0.0/0", "::/0"]
    }
  outbound_rule  {
      protocol           = "udp"
      port_range         = "53"
      destination_addresses   = ["0.0.0.0/0", "::/0"]
    }
  outbound_rule  {
      protocol           = "tcp"
      port_range         = "80"
      destination_addresses   = ["0.0.0.0/0", "::/0"]
    }
    outbound_rule  {
      protocol           = "tcp"
      port_range         = "443"
      destination_addresses   = ["0.0.0.0/0", "::/0"]
    }
    outbound_rule  {
      protocol           = "udp"
      port_range         = "443"
      destination_addresses   = ["0.0.0.0/0", "::/0"]
    }
    outbound_rule  {
      protocol           = "tcp"
      port_range         = "4443"
      destination_addresses   = ["0.0.0.0/0", "::/0"]
    }
    outbound_rule  {
      protocol           = "udp"
      port_range         = "10000"
      destination_addresses   = ["0.0.0.0/0", "::/0"]
    }
}


# Attach the floating IP address to the instance
resource "digitalocean_floating_ip_assignment" "foobar" {
  ip_address = "10.20.30.40"
  droplet_id = digitalocean_droplet.jitsiDroplet.id
}

Now I just run one line to bring the server up:

 terraform apply -var="do_token=xxxxxxx" # If I don't want an approval prompt,
 # I can add "-auto-approve" to the end

And then one line to tear it down:

 terraform destroy -var="do_token=xxxxxxx" # If I don't want an approval prompt,
 # I can add "-auto-approve" to the end

There’s a bunch of stuff I could do better:

  1. It feels wrong having my DNS records point permanently at this fixed IP address that I then dynamically attach to my droplets. Ideally the DNS record itself would be pointed directly at each instance I created, and have a short TTL to react to droplets being created/destroyed. I couldn’t script this, as my DNS isn’t managed within Digital Ocean (or any Terraform-compatible space).
  2. Having the multiline user_data script directly in the Terraform script looks really messy - but for a toy project like this I’d rather just worry about a single uglier file than multiple “better” files.
  3. I’ve hardcoded a (quite big) instance size. This is because the original snapshot was taken on a box this big, and you can’t boot up a droplet from a snapshot that was taken on a bigger instance.
    • I’m probably going to recreate the snapshot on a much smaller droplet so I can deploy using small droplets too.
  4. I could store my Digital Ocean token in a configuration file instead of pasting it in from my password manager.

Real world use & costs

It was a fun project, which I’m finding real usecases for now! When I’m going to have a group video chat with family or friends, I run the terraform one-liner above, and the whole thing is ready within 2 minutes!

I test ran this on a droplet with 6 vCPUs & 16GB RAM for a call with 5 people and the server barely broke a sweat (neither CPU nor RAM usage went above 10%). We had the server up for about 5 hours, for a total cost of $0.60!

You can get $100 credit (valid for 60 days) with Digital Ocean when you first sign up, using either a generic link, or mine