├── .gitignore ├── README.md ├── app.yml ├── base.tf ├── discourse.tf ├── dns.tf └── mail.tf /.gitignore: -------------------------------------------------------------------------------- 1 | *.tfstate 2 | *.tfplan 3 | *.backup 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## discourse-terraform 2 | 3 | This is a guide and a set of Terraform configuration files to automatically 4 | configure [discourse](http://www.discourse.org/). 5 | 6 | You can run the Terraform files with the following command: 7 | 8 | ``` 9 | $ terraform apply \ 10 | -var 'developer_email=YOUR_ACCESS_KEY' \ 11 | -var 'smtp_password=YOUR_SECRET_KEY' \ 12 | -var 'domain=YOUR_DOMAIN' \ 13 | -var 'ssh_key_id=YOUR_SSH_KEY_ID' \ 14 | -var 'do_token=YOUR_DO_TOKEN' \ 15 | -var 'mailgun_token=YOUR_MAILGUN_TOKEN' \ 16 | -var 'ssh_key_path=/path/to/private/key' 17 | ``` 18 | 19 | It's based on the [Discourse easy install guide](https://github.com/discourse/discourse/blob/master/docs/INSTALL-digital-ocean.md). 20 | 21 | ### Running 22 | 23 | Follow the [blog post](http://www.hashicorp.com/blog/terraform-discourse.html). 24 | -------------------------------------------------------------------------------- /app.yml: -------------------------------------------------------------------------------- 1 | ## discourse-terraform Docker configuration template 2 | ## 3 | ## After making changes to this file, you MUST rebuild for any changes 4 | ## to take effect in your live Discourse instance: 5 | ## 6 | ## /var/discourse/launcher rebuild app 7 | ## 8 | 9 | ## this is the all-in-one, standalone Discourse Docker container template 10 | 11 | # You may add rate limiting to by commenting out the ratelimited template. 12 | # Out of the box it allows 12 reqs a second per ip, and 100 per minute per ip 13 | # This is configurable by amending the params in this file 14 | 15 | templates: 16 | - "templates/postgres.template.yml" 17 | - "templates/redis.template.yml" 18 | - "templates/web.template.yml" 19 | - "templates/sshd.template.yml" 20 | 21 | ## which TCP/IP ports should this container expose? 22 | expose: 23 | - "80:80" # fwd host port 80 to container port 80 (http) 24 | - "2222:22" # fwd host port 2222 to container port 22 (ssh) 25 | 26 | params: 27 | db_default_text_search_config: "pg_catalog.english" 28 | ## Set db_shared_buffers to 1/3 of the memory you wish to allocate to postgres 29 | ## on 1GB install set to 128MB on a 4GB instance you may raise to 1GB 30 | db_shared_buffers: "512MB" 31 | # 32 | ## Which Git revision should this container use? (default: tests-passed) 33 | #version: tests-passed 34 | 35 | env: 36 | LANG: en_US.UTF-8 37 | ## TODO: How many concurrent web requests are supported? 38 | ## With 2GB we recommend 3-4 workers, with 1GB only 2 39 | UNICORN_WORKERS: 3 40 | ## 41 | ## TODO: List of comma delimited emails that will be made admin and developer 42 | ## on initial signup example 'user1@example.com, user2@example.com' 43 | DISCOURSE_DEVELOPER_EMAILS: 'SMTPDEVELOPER' 44 | ## 45 | ## TODO: The domain name this Discourse instance will respond to 46 | DISCOURSE_HOSTNAME: 'WEBHOSTNAME' 47 | ## 48 | ## TODO: The mailserver this Discourse instance will use 49 | DISCOURSE_SMTP_ADDRESS: 'SMTPADDRESS' 50 | DISCOURSE_SMTP_PORT: 'SMTPPORT' 51 | DISCOURSE_SMTP_USER_NAME: 'SMTPLOGIN' 52 | DISCOURSE_SMTP_PASSWORD: 'SMTPPASSWORD' 53 | ## 54 | ## The CDN address for this Discourse instance (configured to pull) 55 | #DISCOURSE_CDN_URL: //discourse-cdn.example.com 56 | 57 | ## These containers are stateless, all data is stored in /shared 58 | volumes: 59 | - volume: 60 | host: /var/discourse/shared/standalone 61 | guest: /shared 62 | - volume: 63 | host: /var/discourse/shared/standalone/log/var-log 64 | guest: /var/log 65 | 66 | ## The docker manager plugin allows you to one-click upgrade Discouse 67 | ## http://discourse.example.com/admin/docker 68 | hooks: 69 | after_code: 70 | - exec: 71 | cd: $home/plugins 72 | cmd: 73 | - mkdir -p plugins 74 | - git clone https://github.com/discourse/docker_manager.git 75 | 76 | ## Remember, this is YAML syntax - you can only have one block with a name 77 | run: 78 | - exec: echo "Beginning of custom commands" 79 | 80 | ## If you want to configure password login for root, uncomment and change: 81 | #- exec: apt-get -y install whois # for mkpasswd 82 | ## Use only one of the following lines: 83 | #- exec: /usr/sbin/usermod -p 'PASSWORD_HASH' root 84 | #- exec: /usr/sbin/usermod -p "$(mkpasswd -m sha-256 'RAW_PASSWORD')" root 85 | 86 | ## If you want to authorized additional users, uncomment and change: 87 | #- exec: ssh-import-id username 88 | #- exec: ssh-import-id anotherusername 89 | 90 | - exec: echo "End of custom commands" 91 | - exec: awk -F\# '{print $1;}' ~/.ssh/authorized_keys | awk 'BEGIN { print "Authorized SSH keys for this container:"; } NF>=2 {print $NF;}' 92 | -------------------------------------------------------------------------------- /base.tf: -------------------------------------------------------------------------------- 1 | /* 2 | Variables and providers. Variables without a default are required. 3 | */ 4 | 5 | provider "digitalocean" { 6 | token = "${var.do_token}" 7 | } 8 | 9 | provider "mailgun" { 10 | api_key = "${var.mailgun_key}" 11 | } 12 | 13 | # The email configured as the main admin account 14 | variable "developer_email" {} 15 | 16 | # The SMTP password for the mailgun domain 17 | variable "smtp_password" {} 18 | 19 | # The domain for the discourse app 20 | variable "domain" {} 21 | 22 | # The ID of the SSH key on DigitalOcean 23 | variable "ssh_key_id" {} 24 | 25 | # DigitalOcean access token 26 | variable "do_token" {} 27 | 28 | # Mailgun API key 29 | variable "mailgun_key" {} 30 | 31 | # The path to the SSH key that matches the key on DigitalOcean 32 | variable "ssh_key_path" {} 33 | 34 | # The size of the droplet 35 | variable "size" { 36 | default = "2gb" 37 | } 38 | 39 | # Prints to stdout after completion 40 | output "domain" { 41 | value = "Succesfully set-up Discourse!. Domain: ${digitalocean_domain.discourse.name}" 42 | } 43 | 44 | # Prints to stdout after completion 45 | output "ip" { 46 | value = "Note that DNS propagation can take some time. However, you can visit Discourse via the IP to verify it is running: ${digitalocean_droplet.discourse.ipv4_address}" 47 | } 48 | -------------------------------------------------------------------------------- /discourse.tf: -------------------------------------------------------------------------------- 1 | /* 2 | The Discourse application, provisioning and droplet configuration. 3 | */ 4 | 5 | resource "digitalocean_droplet" "discourse" { 6 | name = "discourse" 7 | image = "ubuntu-14-04-x64" 8 | region = "nyc2" 9 | size = "${var.size}" 10 | ssh_keys = ["${var.ssh_key_id}"] 11 | 12 | connection { 13 | user = "root" 14 | key_file = "${var.ssh_key_path}" 15 | } 16 | 17 | provisioner "file" { 18 | source = "app.yml" 19 | destination = "/root/app.yml" 20 | } 21 | 22 | provisioner "remote-exec" { 23 | inline = [ 24 | "apt-get -y update", 25 | 26 | # Swap 27 | "install -o root -g root -m 0600 /dev/null /swapfile", 28 | "dd if=/dev/zero of=/swapfile bs=1k count=2048k", 29 | "mkswap /swapfile", 30 | "swapon /swapfile", 31 | "echo '/swapfile swap swap auto 0 0' | tee -a /etc/fstab", 32 | "sysctl -w vm.swappiness=10", 33 | "echo vm.swappiness = 10 | tee -a /etc/sysctl.conf", 34 | 35 | # Git 36 | "apt-get -y install git", 37 | 38 | # Docker 39 | "wget -qO- https://get.docker.io/ | sh", 40 | 41 | # Discourse 42 | "mkdir /var/discourse", 43 | "git clone https://github.com/discourse/discourse_docker.git /var/discourse", 44 | "cd /var/discourse", 45 | 46 | # App configuration 47 | "cp /root/app.yml /var/discourse/containers/app.yml", 48 | "sed -i.bak s/SMTPDEVELOPER/${var.developer_email}/g containers/app.yml", 49 | "sed -i.bak s/WEBHOSTNAME/${var.domain}/g containers/app.yml", 50 | "sed -i.bak s/SMTPADDRESS/smtp.mailgun.org/g containers/app.yml", 51 | "sed -i.bak s/SMTPPORT/587/g containers/app.yml", 52 | "sed -i.bak s/SMTPLOGIN/${mailgun_domain.mail.smtp_login}/g containers/app.yml", 53 | "sed -i.bak s/SMTPPASSWORD/${var.smtp_password}/g containers/app.yml", 54 | 55 | # SSH Key for docker container (bootstrap will fail if not existing) 56 | "ssh-keygen -t rsa -f /root/.ssh/id_rsa -q -N ''", 57 | 58 | # Bootstrap and restart 59 | "./launcher bootstrap app", 60 | "./launcher start app" 61 | ] 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /dns.tf: -------------------------------------------------------------------------------- 1 | /* 2 | DNS configuration. This includes 3 | 4 | - DNS record pointing at your DigitalOcean Instance 5 | - DNS MX records for your mail sending, pointing to your mailgun 6 | account. 7 | 8 | You'll need to set the nameservers of your domain to the following: 9 | 10 | ns1.digitalocean.com 11 | ns2.digitalocean.com 12 | ns3.digitalocean.com 13 | */ 14 | 15 | # Creating a DigitalOcean doamin creates an A record by default 16 | resource "digitalocean_domain" "discourse" { 17 | name = "${var.domain}" 18 | ip_address = "${digitalocean_droplet.discourse.ipv4_address}" 19 | } 20 | 21 | 22 | resource "digitalocean_record" "mail_receiving_0" { 23 | domain = "${digitalocean_domain.discourse.name}" 24 | # root 25 | name = "@" 26 | type = "${mailgun_domain.mail.receiving_records.0.record_type}" 27 | value = "${mailgun_domain.mail.receiving_records.0.value}." 28 | priority = 10 29 | } 30 | 31 | resource "digitalocean_record" "mail_receiving_1" { 32 | domain = "${digitalocean_domain.discourse.name}" 33 | # root 34 | name = "@" 35 | type = "${mailgun_domain.mail.receiving_records.1.record_type}" 36 | value = "${mailgun_domain.mail.receiving_records.1.value}." 37 | priority = 10 38 | } 39 | 40 | resource "digitalocean_record" "mail_sending_0" { 41 | domain = "${digitalocean_domain.discourse.name}" 42 | name = "${mailgun_domain.mail.sending_records.0.name}." 43 | type = "${mailgun_domain.mail.sending_records.0.record_type}" 44 | value = "${mailgun_domain.mail.sending_records.0.value}" 45 | priority = 10 46 | } 47 | 48 | resource "digitalocean_record" "mail_sending_1" { 49 | domain = "${digitalocean_domain.discourse.name}" 50 | name = "${mailgun_domain.mail.sending_records.1.name}." 51 | type = "${mailgun_domain.mail.sending_records.1.record_type}" 52 | value = "${mailgun_domain.mail.sending_records.1.value}" 53 | priority = 10 54 | } 55 | 56 | resource "digitalocean_record" "mail_sending_2" { 57 | domain = "${digitalocean_domain.discourse.name}" 58 | name = "${mailgun_domain.mail.sending_records.2.name}." 59 | type = "${mailgun_domain.mail.sending_records.2.record_type}" 60 | value = "${mailgun_domain.mail.sending_records.2.value}." 61 | priority = 10 62 | } 63 | 64 | -------------------------------------------------------------------------------- /mail.tf: -------------------------------------------------------------------------------- 1 | /* 2 | The mail server configuration for sending and receiving emails. 3 | */ 4 | 5 | resource "mailgun_domain" "mail" { 6 | name = "${var.domain}" 7 | spam_action = "disabled" 8 | smtp_password = "${var.smtp_password}" 9 | } 10 | --------------------------------------------------------------------------------