{"id":19838680,"url":"https://github.com/thredded/thredded-ansible","last_synced_at":"2026-02-27T14:44:11.202Z","repository":{"id":151142194,"uuid":"83488362","full_name":"thredded/thredded-ansible","owner":"thredded","description":"A starter set of playbooks for provisioning and deploying a Thredded app.","archived":false,"fork":false,"pushed_at":"2018-07-01T13:27:50.000Z","size":39,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-02-28T22:19:37.415Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thredded.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-02-28T23:06:01.000Z","updated_at":"2018-07-01T13:27:52.000Z","dependencies_parsed_at":null,"dependency_job_id":"8928ca8e-1c45-4e9e-bb72-ebe43145a6ab","html_url":"https://github.com/thredded/thredded-ansible","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/thredded/thredded-ansible","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thredded%2Fthredded-ansible","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thredded%2Fthredded-ansible/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thredded%2Fthredded-ansible/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thredded%2Fthredded-ansible/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thredded","download_url":"https://codeload.github.com/thredded/thredded-ansible/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thredded%2Fthredded-ansible/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29900230,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-27T14:30:36.354Z","status":"ssl_error","status_checked_at":"2026-02-27T14:30:01.989Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T12:18:41.336Z","updated_at":"2026-02-27T14:44:11.177Z","avatar_url":"https://github.com/thredded.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ansible Playbooks for running a Thredded App on Ubuntu 18.04\n\nThis is a starter set of [Ansible](https://www.ansible.com/) playbooks\nfor deploying a Thredded app to an Ubuntu 18.04 server.\n\n**Warning: Experimental, there be dragons.**\n\nThese playbooks provide a good starting point if you're looking to deploy\nThredded to a VPS or to a bare-metal server.\n\nTODO:\n\n1. Email via postfix.\n2. Backups via [backup](https://github.com/backup/backup).\n\n## Table of Contents\n\n* [Overview](#overview)\n   * [Provisioning](#provisioning)\n   * [Deployment](#deployment)\n   * [App compatibility](#app-compatibility)\n* [Usage](#usage)\n* [Deploying to VirtualBox](#deploying-to-virtualbox)\n* [Deploying to production](#deploying-to-production)\n   * [Adding the server to the hosts inventory](#adding-the-server-to-the-hosts-inventory)\n   * [Creating a user for provisioning](#creating-a-user-for-provisioning)\n   * [Provisioning](#provisioning-1)\n   * [Deploying](#deploying)\n   * [Configure HTTPS](#configure-https)\n   * [Enable backups](#enable-backups)\n   * [Configure outgoing email (SMTP)](#configure-outgoing-email-smtp)\n* [How to](#how-to)\n   * [Deploy a new version of the app to the production server](#deploy-a-new-version-of-the-app-to-the-production-server)\n   * [Update the app's environment variables](#update-the-apps-environment-variables)\n   * [See the logs](#see-the-logs)\n   * [Open a rails console on a production server](#open-a-rails-console-on-a-production-server)\n* [Tips](#tips)\n* [Credits](#credits)\n\n## Overview\n\nThere are two playbooks included: `provision.yml` and `deploy.yml`.\n\n### Provisioning\n\nThe provisioning playbook must be run as a user with password-less `sudo` access.\n\nThis playbook ensures the following:\n\nVia the `webapp` role:\n* System-wide dependencies are installed and are up-to-date.\n* The app-specific user exists.\n* The app directory (`/var/www/$APP` by default) exists and belongs to the\n  app-specific user.\n\nVia the `db` role:\n* The Postgresql database server is set up and has a user for the app.\n\nVia the `memcached` role:\n* The memcached server is set up (by default, on the same machine(s) as\n  the webserver(s)).\n\nVia the `nginx` role:\n* The nginx webserver is set up and the app's site configuration is up to date.\n\nVia the `foreman_systemd` role:\n* The app services are managed by `systemd` and their configuration\n  is exported via [foreman](http://ddollar.github.io/foreman/) from `files/Procfile`.\n* The app-specific user has permissions to start and stop app services.\n\nVia the `letsencrypt` role (production only):\n* Let's Encrypt is configured to provide and automatically renew the SSL\n  certificate for the app's webserver.\n\n### Deployment\n\nThe deployment playbook is run as the app-specific user.\n\nThis playbook ensures the following via the `deploy-rails` role:\n\n* The configured Ruby version is installed (via `rbenv`).\n* The app's environment variables are up to date (set in `~/.pam_envinoment`).\n\nThe playbook then fetches the Rails app code from the configured git repository,\ncompiles the assets, runs the migrations, and restarts the app services.\n\n### App compatibility\n\nThese playbooks work out of the box with apps created with\n[thredded_create_app](https://github.com/thredded/thredded_create_app) v0.1.10+.\n\nIf your app was created with an older version of thredded_create_app,\nor was not created with thredded_create_app, you'll need to:\n\n1. Copy the [config/puma.production.rb](https://raw.githubusercontent.com/thredded/thredded_create_app/master/lib/thredded_create_app/tasks/production_configs/puma.production.rb) file.\n\n2. Add the following snippet to the `config/environments/production.rb` file:\n   ```ruby\n   if ENV['MEMCACHE_SERVERS']\n     config.cache_store = :dalli_store,\n         ENV['MEMCACHE_SERVERS'].split(','), {\n             namespace: ENV['APP_ID'] || raise(\"ENV['APP_ID'] not set\"),\n             socket_timeout: 1.5,\n             socket_failure_delay: 0.2,\n             down_retry_delay: 60,\n             pool_size: [2, ENV.fetch('WEB_CONCURRENCY', 3).to_i *\n                            ENV.fetch('MAX_THREADS', 5).to_i].max\n         }\n   end\n   ```\n\n3. Ensure that the `config/database.yml` file uses `ENV['DATABASE_URL']` in production, i.e.:\n  ```yaml\n  production:\n    url: \u003c%= ENV['DATABASE_URL'].inspect if ENV['DATABASE_URL'] %\u003e\n  ```\n\n## Usage\n\nFirst, clone this repo.\n\n```bash\ngit clone https://github.com/thredded/thredded-ansible myapp-ansible\ncd myapp-ansible\n```\n\nThen, copy the example variables file:\n\n```bash\n# Set the APP env var to your app name. Will be used throughout the Readme.\nAPP=myapp\n# Copy the example variables file:\ncp vars/example.yml \"vars/${APP}.yml\"\n```\n\nThen, set the required variables in the variables file.\nAlso, have a look at the `vars/defaults.yml` for the optional variables and\ntheir defaults.\n\nFinally, see the section below to try out your setup in a VM.\n\nOnce you've confirmed it's working in a VM, you can proceed to set up and deploy\nto a real server or a VPS. **The Readme section for this is not ready yet.**\n\n## Deploying to VirtualBox\n\nFirst, install VirtualBox v5.0+ and Vagrant v1.9+.\n\nThen, create a new instance with:\n\n```bash\nvagrant up\n```\n\nThen, **provision** the VM instance:\n\n```bash\nansible-playbook provision.yml -e \"config=vars/${APP}.yml\" -u ubuntu --private-key=.vagrant/machines/vm1/virtualbox/private_key\n```\n\n*If you get an SSH error, verify that you can\n[SSH into the host as the `ubuntu` user](#tips)*.\n\nNow, add the app user SSH key to the app's Git repository deploy keys.\n\n1. Print the key with:\n\n    ```bash\n    ansible webservers -u $APP -a 'cat ~/.ssh/id_rsa.pub'\n    ```\n\n2.  Add the key to the deploy keys in your repository settings.\n\n    On GitLab, the deploy keys settings page is at:\n\n    ```\n    https://gitlab.com/$USER/$APP/deploy_keys\n    ```\n\n    On GitHub, it's at:\n\n    ```\n    https://github.com/$USER/$APP/settings/keys\n    ```\n\nThen, **deploy** to the VM instance:\n\n```bash\nansible-playbook deploy.yml -e \"config=vars/${APP}.yml\"\n```\n\nThis will run the migrations, but you might also want to seed the database\nif deploying for the first time. You can do so by running:\n\n```bash\nansible 'webservers[0]' -u $APP -a \"chdir=/var/www/$APP/current bundle exec rails db:seed\"\n```\n\nCongratulations! You can now open the app at http://localhost:8080 and log in as\n`admin@$APP.com` with password `123456`.\n\n## Deploying to production\n\nThe guide describes provisioning deploying to a single production server.\n\nThe process is largely identical to deploying to Vagrant, but we will also:\n\n1. Enable HTTPS via letsencrypt with automated certificate renewal.\n2. Enable daily database backups using [backup]. **TODO**\n3. Configure the [postfix] mail transfer agent to send email. **TODO**\n\nThis guide focuses on the differences from the VirtualBox guide above and\nassumes that you have already followed the VirtualBox guide.\n\n[backup]: https://github.com/backup/backup\n[postfix]: http://www.postfix.org/\n\n### Adding the server to the hosts inventory\n\nFirst, we need to create the hosts inventory file for our production hosts:\n\n1. Copy the example hosts file:\n\n   ```bash\n   cp hosts/example hosts/${APP}-prod\n   ```\n\n2. Set the host IP address in the `hosts/${APP}-prod` file.\n\nWe will need to tell Ansible to use this inventory file by passing\n`-i hosts/${APP}-prod` to every `ansible` and `ansible-playbook` command.\nAlternatively, you can change the default inventory file in `ansible.cfg`.\n\n### Creating a user for provisioning\n\nIf you've created a new server with something like [Scaleway](https://scaleway.com)\n(offers a 3 € a month VPS large enough for Thredded) then the server by default\nwill only have a root user with SSH key authentication, so you can skip this step.\n\n**If** you do not have password-less SSH-as-root, or you'd prefer to not use\nthe root user (e.g. if you work in a team), you can create another user instead\nlike this:\n\n1. Create a user with the same name as your local user on the production servers.\n   Omit the `password` argument if you're sure you'll never lose your SSH key.\n\n   ```bash\n   ansible all -i hosts/${APP}-prod -u root -m user -a \\\n     \"name=$USER groups=sudo password=$(mkpasswd --method=sha-512)\"\n   ```\n\n2. Authorize your SSH key to connect as `$USER`:\n\n   ```bash\n   ansible all -i hosts/${APP}-prod -u root -m authorized_key -a \\\n     \"user=$USER key={{lookup('file', '~/.ssh/id_rsa.pub')}}\"\n   ```\n\n3. Allow the newly created user to run `sudo` **without password**:\n\n   ```bash\n   ansible all -i hosts/${APP}-prod -u root -m lineinfile -a \\\n     \"path=/etc/sudoers.d/$USER create=yes line='$USER ALL=(ALL) NOPASSWD:ALL' validate='/usr/sbin/visudo -cf %s'\"\n   ```\n\n4. Pass `-u $USER` instead of `-u root` to Ansible in the\n   *Provisioning* commands below.\n\n### Provisioning\n\nCopy the vars config file you've created earlier for Vagrant, as we'll need the\nconfiguration to be slightly different (we'll change it afterwards).\n\n```bash\ncp vars/${APP}.yml vars/${APP}-prod.yml\n```\n\nYou may want to change all the passwords and the secret key in the new config.\n\nThen, run the provisioning playbook:\n\n```bash\nansible-playbook provision.yml -u root -e \"config=vars/${APP}-prod.yml\" -i hosts/${APP}-prod\n```\n\n### Deploying\n\n1. Add the SSH key of the app user to your git repository deploy keys.\n   To print the key, run:\n\n   ```bash\n   ansible webservers -i hosts/${APP}-prod -u $APP -a 'cat ~/.ssh/id_rsa.pub'\n   ```\n\n2. Run the deployment playbook:\n\n   ```bash\n   ansible-playbook deploy.yml -i hosts/${APP}-prod -e \"config=vars/${APP}-prod.yml\"\n   ```\n\n3. Seed the database:\n\n   ```bash\n   ansible 'webservers[0]' -i hosts/${APP}-prod -u $APP -a \"chdir=/var/www/$APP/current bundle exec rails db:seed\"\n   ```\n\nCongratulations! You can now open the app at the server's IP and log in as\n`admin@$APP.com` with password `123456`. Change the password immediately.\n\n### Configure HTTPS\n\nNext, we will configure HTTPS with Let's Encrypt, including certificate\nauto-renewal.\n\nFor this, you will need to have `{{app_host}}` and `www.{{app_host}}`\nconfigured as the A-records for your server IP.\n\nFirst, in the `vars/${APP}-prod.yml` file, set the `app_host` and uncomment the\n*HTTPS with Let's encrypt* section.\n\nThen, run the provisioning playbook:\n\n```bash\nansible-playbook provision.yml -u root -e \"config=vars/${APP}-prod.yml\" -i hosts/${APP}-prod\n```\n\nThat's it! Verify that `https://{{app_host}}` works, and that these variations\nall redirect to that URL: `http://{{app_host}}`, `http://www.{{app_host}}`,\nand `https://www.{{app_host}}`.\n\nTest the quality of your HTTPS configuration at [SSL Labs](https://ssllabs.com/ssltest/).\n\n### Enable backups\n\nTODO\n\n### Configure outgoing email (SMTP)\n\nYou'll also need to configure your app to send email correctly.\n\nOne way to do this is by using a 3-rd party SMTP server such as GMail or Zoho.\n\nGMail requires a paid Google Apps for Business suite for use with a custom\ndomain, while [Zoho](https://www.zoho.com/) is free.\n\nOnce you configure your domain with Zoho, here is the email configuration\nyou can put in `config/environments/production.rb`:\n\n```ruby\nconfig.action_mailer.default_url_options = { host: ENV['APP_HOST'], protocol: 'https' }\nconfig.action_mailer.perform_deliveries = true\nconfig.action_mailer.raise_delivery_errors = true\nconfig.action_mailer.delivery_method = :smtp\nconfig.action_mailer.smtp_settings = {\n  address:                'smtp.zoho.com',\n  port:                   587,\n  domain:                 ENV['APP_HOST'],\n  user_name:              ENV['SMTP_USER'],\n  password:               ENV['SMTP_PASSWORD'],\n  authentication:         :plain,\n  enable_starttls_auto:   true,\n}\n```\n\nYou also need to set `Thredded.email_from` in the Thredded initializer,\nand `config.mailer_sender` in the devise initializer.\n\nThen, set `SMTP_USER/PASSWORD` under `app_user_env` in `vars/${APP}-prod` and deploy.\n\n**NB:** Many VPS hosting providers, such as Scaleway and DigitalOcean, block\noutgoing connections to 587 by default. Make sure to change this in the settings.\nFor Scaleway, see [this FAQ entry](https://www.scaleway.com/faq/server/network/#-I-cannot-send-any-email).\n\nZoho free tier is limited to 1,000 emails sent per day (up to 5,000 on paid plans),\nwhile GMail is limited to [2,000 / day](https://support.google.com/a/answer/166852?hl=en),\nso you will want to use a specialized service like [Mailgun](https://www.mailgun.com/),\nor host your own server once you exceed that. **TODO: Postfix role.**\n\n## How to\n\n### Deploy a new version of the app to the production server\n\nRun the deployment playbook:\n\n```bash\nansible-playbook deploy.yml -i hosts/${APP}-prod -e \"config=vars/${APP}-prod.yml\"\n```\n\n### Update the app's environment variables\n\nUpdate the `vars/$APP.yml` file and either do a full deploy, or run:\n\n```bash\nansible-playbook deploy.yml -e \"config=vars/${APP}.yml\" --tags env\n```\n\nThe command above will only update the environment and then restart the app.\n\n**NB**: Removing an environment variable from `vars/$APP.yml` does **not**\nremove it from the environment file on the server (`~$APP/.pam_environment`).\n\n### See the logs\n\nThe application and webserver logs are located at `/var/www/$APP/shared/log`.\nThe app service output (such as errors when starting the services)\nis logged to `/var/log/syslog`.\n\n### Open a `rails console` on a production server\n\nIn production:\n\n```bash\n# Replace \"${IP}\" with the server's IP address\nssh ${APP}@${IP} -t \"cd /var/www/$APP/current \u0026\u0026 bundle exec rails c\"\n```\n\nTo print a server IP address / hostname:\n\n```bash\nansible 'webservers[0]' -i hosts/${APP}-prod -m debug -a 'var=ansible_host' | grep -oE \"[0-9.]{2,}\"\n```\n\nOn VirtualBox:\n\n```bash\nssh $APP@127.0.0.1 -p 2222 -t \"cd /var/www/$APP/current \u0026\u0026 bundle exec rails c\"\n```\n\n## Tips\n\nYou can ssh into the VirtualBox instance on port 2222:\n\n```bash\n# As the \"ubuntu\" user (this user can run sudo without a password)\nssh ubuntu@127.0.0.1 -p2222 -i .vagrant/machines/vm1/virtualbox/private_key\n# As the app user\nssh $APP@127.0.0.1 -p2222\n```\n\nTo run a command as the web user via Ansible:\n\n```bash\n# Use webservers[0] for just one of the servers\nansible webservers -u $APP -a \"chdir=/var/www/$APP/current bundle exec rails db:seed\"\n```\n\nTo see the output of commands as they run,\npass the `-vvv` flag to `ansible` or `ansible-playbook`.\n\n## Credits\n\n* The Rails deployment role is based on: https://github.com/nicolai86/ansible-rails-deployment.\n* Rbenv installation is based on: https://github.com/erasme/ansible-rbenv.\n* The Nginx role is based on: https://github.com/jdauphant/ansible-role-nginx.\n* The Memcached role is based on: https://github.com/geerlingguy/ansible-role-memcached.\n* The Nginx HTTPS config is based on: http://stackoverflow.com/a/41948807/181228\n* Let's encrypt HTTPS certs and auto-renewal are based on:\n  * https://github.com/thefinn93/ansible-letsencrypt\n  * https://gist.github.com/mattiaslundberg/ba214a35060d3c8603e9b1ec8627d349\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthredded%2Fthredded-ansible","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthredded%2Fthredded-ansible","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthredded%2Fthredded-ansible/lists"}