{"id":13426910,"url":"https://github.com/forem/selfhost","last_synced_at":"2025-05-15T19:08:53.179Z","repository":{"id":41870168,"uuid":"371127045","full_name":"forem/selfhost","owner":"forem","description":"Selfhost your Forem Community on your own infrastructure 🎉","archived":false,"fork":false,"pushed_at":"2023-12-21T00:15:30.000Z","size":123,"stargazers_count":1466,"open_issues_count":13,"forks_count":181,"subscribers_count":45,"default_branch":"main","last_synced_at":"2025-04-08T00:38:46.575Z","etag":null,"topics":["ansible","butane","fcos","forem","ignition"],"latest_commit_sha":null,"homepage":"https://www.forem.com/get-started/","language":"Jinja","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/forem.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-05-26T18:04:47.000Z","updated_at":"2025-03-28T13:17:58.000Z","dependencies_parsed_at":"2024-01-10T22:10:34.382Z","dependency_job_id":"4dc09074-a35c-4087-8c2c-55e4c0e812c2","html_url":"https://github.com/forem/selfhost","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forem%2Fselfhost","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forem%2Fselfhost/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forem%2Fselfhost/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forem%2Fselfhost/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/forem","download_url":"https://codeload.github.com/forem/selfhost/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254404357,"owners_count":22065641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["ansible","butane","fcos","forem","ignition"],"created_at":"2024-07-31T00:01:48.195Z","updated_at":"2025-05-15T19:08:53.152Z","avatar_url":"https://github.com/forem.png","language":"Jinja","funding_links":[],"categories":["Jinja"],"sub_categories":[],"readme":"\n# Forem Self-Host\nThis is a repo for setting up a free, self-managed install of [Forem](https://github.com/forem/forem) on a [Fedora CoreOS](https://getfedora.org/en/coreos) VM running on one of a few popular cloud providers (current support for [DigitalOcean](https://www.digitalocean.com/), [AWS](https://aws.amazon.com/), and [Google Cloud](https://cloud.google.com/)). Local development is also supported using a VM on Linux via [QEMU](https://www.qemu.org/).\n\n**Please note that Forem is a complex piece of software, and hosting and managing it in a cloud environment is non-trivial.** While the recipes and scripts here are expected to work for the limited scenarios we tested against, use and modification of the recipes, or altering the deployed environment, may require familiarity with the following layers of the tech stack we built with, and ongoing maintenance of the deployed system may require interacting with any of these technologies:\n- [Ansible](https://www.ansible.com/)\n- Your chosen cloud provider - both CLI and UI use\n- [Python 3 and pip3](https://www.python.org/)\n- [systemd](https://www.freedesktop.org/wiki/Software/systemd/)\n- [Podman](https://podman.io/)\n- General Linux administration, especially [Fedora CoreOS](https://getfedora.org/en/coreos), including:\n  - [Butane](https://coreos.github.io/butane/)\n  - [Ignition](https://coreos.github.io/ignition/)\n\n**If a Self-Hosted Forem is not right for you, we offer a fully-managed, enterprise solution called Forem Cloud; no technical setup required. For more information, [please contact us via this form](https://formkeep.com/p/cfa67316d1c12d23ecb3c08b359f944b).**\n\nFor those that want to DIY beyond the scope of this repo, you can use the systemd units in the [Butane template](https://github.com/forem/selfhost/blob/main/playbooks/templates/forem.yml.j2) as an example of how to run Forem without Fedora CoreOS on a Linux distribution that supports systemd, or customize that template to fit your needs or create a bootable Ignition configuration to consume on bare metal or in a custom VM.\n\nThe goal of this project is to provide you with the choice, freedom, and cost-effectiveness to host your own Forem community as you see fit.\n\nWe can't wait to see the community you selfhost with Forem!\n\n## Table of Contents\n- [Forem Self-Host](#forem-self-host)\n  * [Requirements](#requirements)\n  * [Quick Start](#quick-start)\n    + [Semi-automated setup](#semi-automated-setup)\n    + [Manual installation](#manual-installation)\n  * [Provisioning Targets](#provisioning-targets)\n    + [AWS](#aws)\n      - [Setup](#setup)\n      - [Provision](#provision)\n    + [DigitalOcean](#digitalocean)\n      - [Setup](#setup-1)\n      - [Provision](#provision-1)\n    + [Google Cloud](#google-cloud)\n      - [Setup](#setup-2)\n      - [Provision](#provision-2)\n  * [Ansible Dynamic Inventories](#ansible-dynamic-inventories)\n    + [AWS](#aws-1)\n    + [DigitalOcean](#digitalocean-1)\n    + [Google Compute](#google-compute)\n  * [Configuration Internals](#configuration-internals)\n    + [systemd](#systemd)\n    + [Forem configs](#forem-configs)\n  * [SSH Examples](#ssh-examples)\n    + [foremctl](#foremctl)\n    + [Update Forem to the latest version and restart](#update-forem-to-the-latest-version-and-restart)\n    + [foremimg](#foremimg)\n    + [Set the Forem container repository and tag](#set-the-forem-container-repository-and-tag)\n    + [Update Forem to the latest version with no restart](#update-forem-to-the-latest-version-with-no-restart)\n    + [Rollback Forem to the last running version and restart](#rollback-forem-to-the-last-running-version-and-restart)\n    + [Update Fedora CoreOS to the latest stable version](#update-fedora-coreos-to-the-latest-stable-version)\n    + [Backup your Forem data](#backup-your-forem-data)\n  * [Development](#development)\n    + [Install Dependencies](#install-dependencies)\n      - [SSH Key](#ssh-key)\n      - [SSH Config](#ssh-config)\n      - [Ansible Vault](#ansible-vault)\n      - [Create Forem Ansible Inventory](#create-forem-ansible-inventory)\n      - [Launch Forem Locally](#launch-forem-locally)\n      - [Local SSH Access](#local-ssh-access)\n\n## Requirements\n\n- Git\n- [Python 3.x](https://www.python.org/downloads/) and pip3\n    - macOS: `brew install python3`\n    \u003e **Note**: This will likely use **Python 3** at `/usr/local/bin/python3`, _not_ `/usr/bin/python3`, requiring that you set `ansible_python_interpreter` to `/usr/local/bin/python` in inventory or via extra vars (eg `-e ansible_python_interpreter=/usr/local/bin/python`)\n- [Ansible](https://docs.ansible.com/ansible/latest/installation_guide/index.html): `ansible-core` 2.11 or greater (provided by Ansible 4.0.0)\n- [Butane](https://github.com/coreos/butane/blob/master/docs/getting-started.md#getting-butane)\n    - Mac OS: `brew install butane`\n- `pwgen`\n    - Mac OS: `brew install pwgen`\n- Fedora CoreOS, running on the [Stable stream](https://getfedora.org/en/coreos?stream=stable)\n- A supported cloud provider, bare metal server, or a VM in QEMU.\n\n*Note: Some provisioning targets have additional requirements that are detailed out in each respective section.*\n\n## Quick Start\n\n_Note: Following this quick start guide with the cloud provider of your choice will cost you money! Please consult with each cloud provider to figure out how much your Forem will cost you per month._\n\nStart by cloning the `forem/selfhost` repository to your local computer and change into the newly created directory:\n\n```\ngit clone https://github.com/forem/selfhost.git\ncd selfhost\n```\n\nAfter this step you have two choices: a semi-automated setup via a script or a completely manual installation.\n\n### Semi-automated setup\n\nWe have a script in place that will perform several of the necessary setup tasks for you. It will take care of the first 3 steps of the manual installation process (installing Python dependencies, generating an Ansible Vault password and copying the inventory definition for you). It will also generate the secrets needed for step 4.\n\n```\n./setup\n\nVerifying that pip is available\n\nInstalling Python dependencies\n[output omitted]\n\nGenerating Ansible Vault secret\nixooGe3ob0shob8soo6AhYie\n\nCopying example inventory\n'inventory/example/setup.yml' -\u003e 'inventory/forem/setup.yml'\n\nGenerating Vault secrets\n[output omitted]\n\nUse these secrets to replace the placeholders in inventory/forem/setup.yml\n```\n\nOnce the script finished running, continue from step 4. of the manual installation process described below.\n\n### Manual installation\n\n1) Install Python dependencies:\n    - System-wide:\n        - `pip3 install -r requirements.txt`\n    - In a virtual environment: create a virtual env first, then enter it before installing the dependencies. Then remain in the virtual env unless you're done with the setup process. Example with Python 3's native [`venv` module](https://docs.python.org/3/library/venv.html):\n        - `python3 -m venv /path/to/new/virtual/environment`\n        - `source \u003cvenv\u003e/bin/activate`\n        - `pip3 install -r requirements.txt`\n        - perform rest of setup\n        - `deactivate` (leaves the virtual env)\n1) Generate an Ansible Vault password\n    - `mkdir -p ~/.config/forem`\n    - `pwgen -1 24|tee ~/.config/forem/selfhost_ansible_vault_password`\n1) Copy example Ansible Inventory from `inventory/example/setup.yml` to `inventory/forem/setup.yml`\n1) Edit `inventory/forem/setup.yml` Ansible Inventory with your Forem settings\n    - Edit the following Ansible inventory variables:\n        - default_email (Admin Email for system to use)\n        - forem_domain_name (A domain name that you own and set A records on at your DNS provider)\n        - forem_subdomain_name (defaults to www)\n        - forem_server_hostname (defaults to host)\n    - If you used the setup script you can use the previously generated inventory secrets here. Otherwise, you have to use  [`ansible-vault encrypt_string`](https://docs.ansible.com/ansible/latest/user_guide/vault.html#encrypting-individual-variables-with-ansible-vault) to create the secrets listed below. See [\"Required Ansible Vault secret variables\" in the example setup.yml](https://github.com/forem/selfhost/blob/main/inventory/example/setup.yml#L67), which contains the required commands to generate each variable's value:\n        - vault_secret_key_base\n        - vault_imgproxy_key\n        - vault_imgproxy_salt\n        - vault_forem_postgres_password\n1) Setup SSH access for your cloud provider\n    - If you choose to use **DigitalOcean or Google Cloud** as your cloud provider, you will need to generate a [SSH key](https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#generating-a-new-ssh-key) and save it to `${HOME}/.ssh/forem`. Use `ls -lh ~/.ssh/forem*` to ensure you have both a `${HOME}/.ssh/forem` private key, and a corresponding `${HOME}/.ssh/forem.pub` public key.\n   - If you use **AWS** as your cloud provider, you will need to generate an RSA-based SSH key and save it to the file path `~/.ssh/id_rsa.pub.`\n   Please visit the **AWS RSA based SSH key** section in our [Self Host: Quick Start in Depth](https://forem.dev/foremteam/self-host-quick-start-in-depth-2165) guide for instructions on creating an RSA-based key.\n1) Pick a supported cloud provider and set it up on your workstation\n    - [AWS](https://github.com/forem/selfhost#aws)\n    - [DigitalOcean](https://github.com/forem/selfhost#digitalocean)\n    - [Google Cloud](https://github.com/forem/selfhost#google-compute)\n1) Run the Ansible Playbook for your chosen cloud provider\n    - [AWS](https://github.com/forem/selfhost#provision)\n    - [DigitalOcean](https://github.com/forem/selfhost#provision-1)\n    - [Google Cloud](https://github.com/forem/selfhost#provision-2)\n1) Once your Forem VM is set up with your chosen cloud provider, you will need to point DNS at the IP address that is output at the end of the provider playbook.\n1) Once DNS is pointed at your Forem VM, you will need to restart the Forem Traefik service (`sudo systemctl restart forem-traefik.service`) [via SSH on your Forem server](https://github.com/forem/selfhost#ssh-examples) to generate a TLS cert.\n1) Go to your Forem domain name and create your first account. Please see the Forem Admin documentation located [here](https://admin.forem.com/) for more information on setting up your Forem.\n\n----\n\n## Provisioning Targets\n\n**Note about recommended instance types and cost:** for each hosted provisioning target below, we attempted to recommend an instance type with 2 CPUs, 2GB of RAM, and a monthly cost of around $15 USD. Please note that providers may charge additionally for disk space, network usage, etc, so your price per month may vary based on your Forem's usage and needs. For exact and specific pricing information, please see each provider directly.\n\n----\n### AWS\n\nThe AWS provisioning target has a few preset variables that can be either edited in the playbook or passed along as Ansible [extra vars](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#defining-variables-at-runtime) on the CLI.\n\n```\nfcos_aws_region: us-east-1\nfcos_aws_size: t3a.small\nfcos_aws_ebs_size: 100\nssh_key: \"{{ lookup('file', '~/.ssh/id_rsa.pub') }}\"\n```\n\n- `fcos_aws_region`: the AWS region that is used to setup your Forem server. The default region is in `us-east-1` which is in North Virginia, USA\n- `fcos_aws_size`: the AWS EC2 instance type. A recommended type is a `t3a.small` EC2 instance, with 2 VCPUs and 2GB of RAM\n- `fcos_aws_ebs_size`: the amount of EBS disk space (in GB)\n- `ssh_key`: the path to a public SSH key. Note that AWS's EC2 service can only use RSA based SSH keys. If you get an error that your SSH key is not the right type, please generate an RSA based SSH key and set `ssh_key` with a lookup path to that key\n\n#### Setup\n1) Install the [Ansible Amazon AWS collections](https://github.com/ansible-collections/amazon.aws) `ansible-galaxy collection install amazon.aws community.aws` or install them via `ansible-galaxy collection install -r requirements.yml`\n2) Download and install the [AWS CLI version 2](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) tool\n3) Install `boto`, `boto3`, and `botocore` pip3 modules `pip3 install boto boto3 botocore` or run `pip3 install -r requirements.txt`\n4) [Create an AWS IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html#id_users_create_console) with Programmatic access called `forem-selfhost` with the following `AmazonEC2FullAccess`, `AmazonS3FullAccess`, `AmazonVPCFullAccess` AWS managed policies attached. Be sure to save the Access key ID and Secret access key to use in step 5.\n5) Run `aws configure --profile forem-selfhost` and input the access key ID and secret key when prompted. We use `us-east-1` for default region name but you can choose a different one if you wish. Set default output format to `json`\n\n#### Provision\n1) Run the AWS provider playbook to setup your Forem\n    - `ansible-playbook -i inventory/forem/setup.yml playbooks/providers/aws.yml`\n\n----\n### DigitalOcean\n\nThe DigitalOcean provisioning target has a few preset variables that can be either edited in the playbook or pass along as Ansible [extra vars](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#defining-variables-at-runtime) on the CLI.\n\n```\nforem_do_region: nyc3\nforem_do_size: s-2vcpu-2gb\n```\n\n- `forem_do_region`: the DigitalOcean region that is used to setup your Forem server. The default region is `nyc3` which is in New York City, New York, USA\n- `forem_do_size`: the Droplet size. The recommended size is `s-2vcpu-2gb`, with 2 Shared CPUs, 2GB of RAM, a 60GB SSD disk, and 3TB of outbound transfer.\n\n#### Setup\n1) Install the [DigitalOcean Ansible collection](https://github.com/ansible-collections/community.digitalocean) `ansible-galaxy collection install community.digitalocean` or install it via `ansible-galaxy collection install -r requirements.yml`\n2) [Download and install](https://docs.digitalocean.com/reference/doctl/how-to/install/) `doctl`\n3) [Create DigitalOcean Auth Token](https://docs.digitalocean.com/reference/api/create-personal-access-token/)\n4) Run [`doctl auth init`](https://docs.digitalocean.com/reference/doctl/reference/auth/init/) and pass the API token created from step 3 and verify that you can authenticate to the DigitalOcean API with `doctl account get`. If you used a context here, you'll need to also `doctl auth switch`.\n\n#### Provision\n1) Run the DigitalOcean provider playbook to set up your Forem\n    - `ansible-playbook -i inventory/forem/setup.yml playbooks/providers/digitalocean.yml`\n\n**Note**: DigitalOcean does not have support for Fedora CoreOS. We have to upload a custom image to your account via Ansible. If the \"`Wait for fcos-{{ fcos_download_release }} to be created`\" task times out. please check the [Custom Images](https://cloud.digitalocean.com/images/custom_images) section on your DigitalOcean account to see if your image is still in a pending state. Wait for it to finish processing and re-run the DigitalOcean provider playbook.\n\n----\n### Google Cloud\n\nThe Google Cloud provisioning target has a few preset variables that can be either edited in the playbook or pass along as Ansible [extra vars](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#defining-variables-at-runtime) on the CLI.\n\n```\nforem_gcp_region: us-central1\nforem_gcp_zone: a\nforem_gcp_machine_type: e2-small\nforem_gcp_disk_size: 100\nforem_gcp_project_id: forem-selfhost-12345\n```\n\n- `forem_gcp_region` + `forem_gcp_zone`: the Google Cloud region and zone that is used to setup your Forem server. The default region is `us-central1` in zone `a` which is in Council Bluffs, Iowa, USA\n- `forem_gcp_machine_type`: the GCP machine type. A recommended type is `e2-small`, with 2 shared CPUs and 2GB of RAM\n- `forem_gcp_disk_size`: the amount of disk space (in GB)\n- `forem_gcp_project_id`: your GCP project ID\n\n#### Setup\n1) Install the Google Cloud collection `ansible-galaxy collection install google.cloud` or install it via `ansible-galaxy collection install -r requirements.yml`\n2) Install `requests` and `google-auth` pip3 modules `pip3 install requests google-auth` or run `pip3 install -r requirements.txt`\n3) Create a [Google Cloud Service Account](https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount) called `forem-selfhost` with Compute Instance Admin (v1) privileges and [download a JSON credentials file](https://support.google.com/cloud/answer/6158849?hl=en\u0026ref_topic=6262490#serviceaccounts) and place it in `~/.gcp/forem.json`\n\n#### Provision\n1) Run the Google Cloud provider playbook to setup your Forem\n    - `ansible-playbook -i inventory/forem/setup.yml playbooks/providers/gcp.yml`\n\n----\n## Ansible Dynamic Inventories\n\nWe provide some example [Dynamic Inventories](https://docs.ansible.com/ansible/latest/user_guide/intro_dynamic_inventory.html) for you to use on your self-hosted Forem. You can use them to run [Ansible Adhoc Commands](https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html) or write your own Ansible playbooks to manage your Forem.\n\nSee the [SSH Examples](https://github.com/forem/selfhost#ssh-examples) for some commands that you can run with an Ansible Adhoc command.\n\n### AWS\n\n*Show all Forems on AWS*\n```\nansible-inventory -i inventory/providers/aws/ --graph forem\n\n```\n\n*Run an Ansible Adhoc command on all Forems on AWS*\n```\nansible -i inventory/providers/aws/ -m command -a \"hostname\" forem\n\n```\n### DigitalOcean\n\n_Note: You need to run `export DO_API_TOKEN=your_digitalocean_api_token` before running the `ansible-inventory` or `ansible` commands!_\n\n*Show all Forems on DigitalOcean*\n```\nansible-inventory -i inventory/providers/digitalocean/ --graph forem\n\n```\n\n*Run an Ansible Adhoc command on all Forems on DigitalOcean*\n```\nansible -i inventory/providers/digitalocean/ -m command -a \"hostname\" forem\n\n```\n\n### Google Compute\n\n_Note: You need to edit the `project` list in `inventory/providers/gcp/gcp.yml` with your GCP project for this Ansible Inventory Dynamic to work correctly!_\n\n*Show all Forems on Google Compute*\n```\nansible-inventory -i inventory/providers/gcp/ --graph forem\n\n```\n\n*Run an Ansible Adhoc command on all Forems on Google Compute*\n```\nansible -i inventory/providers/gcp/ -m command -a \"hostname\" forem\n\n```\n\n----\n## Configuration Internals\n\nThis section covers how Forem is configured and run on Fedora CoreOS.\n\n### systemd\nForem is run with a stack of containers that are powered via [Podman](https://podman.io/) and [systemd](https://systemd.io/). The systemd unit files are located in `/etc/systemd/system`:\n\n```\n$ cd /etc/systemd/system\n$ ls -lah forem*\n-rw-r--r--. 1 root root  243 Jun 29 17:16 forem-container.service\n-rw-r--r--. 1 root root  833 Jun 29 17:16 forem-imgproxy.service\n-rw-r--r--. 1 root root 1.1K Jun 29 17:16 forem-openresty.service\n-rw-r--r--. 1 root root  787 Jun 29 17:16 forem-pod.service\n-rw-r--r--. 1 root root  904 Jun 29 17:16 forem-postgresql.service\n-rw-r--r--. 1 root root 1.4K Jun 29 17:16 forem-rails.service\n-rw-r--r--. 1 root root  941 Jun 29 17:16 forem-redis.service\n-rw-r--r--. 1 root root  951 Jun 29 17:16 forem-traefik.service\n-rw-r--r--. 1 root root 1006 Jun 29 17:16 forem-worker.service\n-rw-r--r--. 1 root root  691 Jun 29 17:16 forem.service\n```\n\nWe use [systemd unit dependencies](https://fedoramagazine.org/systemd-unit-dependencies-and-order/) heavily to correctly configure the start of service required to power your Forem.\n\nThe first systemd unit that runs on boot is `forem-container.service`. This service interfaces with `foremimg` to pull down the Forem container image and ensure that the `localhost/forem/forem:current` container tag is present.\n\nWe then create a [Podman pod](https://developers.redhat.com/blog/2019/01/15/podman-managing-containers-pods) with the `forem-pod.service` to run all of the Forem services within it. Pods are a group of one or more containers that share the same network, pid and ipc namespaces. This means that `localhost` is isolated inside the pod and shared across all of the containers within the pod. The pod also binds ports `80` and `443` on the Fedora CoreOS server.\n\nWe then launch all of the required services within this Podman pod: `forem-imgproxy.service`, `forem-postgresql.service`, `forem-redis.service`. All of these services use the systemd [BindsTo](https://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo=) directive which configures a strong dependency on `forem-pod.service`. This means if `forem-pod.service` is stopped or it enters an inactive state, all of these services will stop too. Also, all of these services have to be up before our next unit, `forem.service` can start successfully.\n\nThe `forem.service` unit uses the [BindsTo](https://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo=) directive to bind `forem-rails.service`, `forem-worker.service`, and `forem-openresty` together as they are tightly dependent on each other to run Forem. This means you can stop and start the `forem.service` unit via `systemctl` and it will stop the three main Forem units, too. This service also ensures that `forem-pod.service`, `forem-postgresql.service`, `forem-imgproxy.service`, and `forem-redis.service` units are active and that the local container `localhost/forem/forem:current` exists before starting.\n\nThe main Forem systemd units are `forem-rails.service`, `forem-worker.service`, and `forem-openresty`. The `forem-rails.service` creates container volume mount `/opt/forem/data/uploads` on the Fedora CoreOS host and mounts it inside the container at `/opt/apps/forem/public/uploads`. The Forem [Containerfile](https://github.com/forem/forem/blob/main/Containerfile#L76) uses a `VOLUME` directive to create a container volume `/opt/apps/forem/public` and it puts all of the Forem public assets (CSS and JS) inside. These volumes are shared between the other main Forem containers: `forem-worker.service`, and `forem-openresty`.\n\nThe `forem-rails.service` is the main Ruby on Rails application that is running [Puma](https://puma.io/) which is a very fast and concurrent HTTP 1.1 application server.\n\nThe `forem-worker.service` is the background worker container that runs [Sidekiq](https://github.com/mperham/sidekiq).\n\nThe `forem-openresty.service` runs OpenResty which is a dynamic web platform based on NGINX and LuaJIT. We use OpenResty to proxy connections to Puma in the `forem-rails.service` unit. We also use OpenResty to send proxy requests to `forem-imgproxy.service` for image resizing, which are then cached in OpenResty.\n\nThe last service we use in the configuration phase is `forem-traefik.service`. It is responsible for handling traffic from the Internet and passing it into the Forem Pod to `forem-openresty.service`, which then manages the traffic to `forem-rails.service`. It also manages the TLS certificate from [Let's Encrypt](https://letsencrypt.org/docs/) and handles the redirection from HTTP to HTTPS.\n\n### Forem configs\n\nAll of your Forem's data and configuration resides in `/opt/forem`. This is the most important directory on your Forem. You should backup this directory regularly.\n\n```\n# ll /opt/forem/\ntotal 4\ndrwxr-x---. 3 root root 39 Jun 29 17:16 configs\ndrwxr-x---. 5 root root 52 Jun 29 17:16 data\ndrwxr-x---. 2 root root 82 Jun 29 17:16 envs\ndrwxr-xr-x. 2 root root  6 Jun 29 17:16 tmp\n-rw-r--r--. 1 root root 37 Jun 29 17:16 version\n\n```\n\nThe `configs` directory contains the OpenResty (Nginx) configuration file and the Traefik configuration TOML files, along with the `acme.json`, which holds the TLS certificate from Let's Encrypt.\n\n```\n# ll\ntotal 4\n-rw-r--r--. 1 root root 2375 Jun 29 17:16 nginx.conf\ndrwxr-x---. 2 root root   63 Jun 29 17:16 traefik\n# ll traefik/\ntotal 12\n-rw-------. 1 root root 3524 Jun 29 17:20 acme.json\n-rw-r--r--. 1 root root 1928 Jun 29 17:16 dynamic.toml\n-rw-r--r--. 1 root root  740 Jun 29 17:16 traefik.toml\n```\n\nThe `data` directory contains your `postgresql`, `redis`, and `upload` directories. This directory contains all of your Forem content including your members.\n\n```\n# ll data/\ntotal 4\ndrwx------. 19 polkitd root 4096 Jun 29 17:19 postgresql\ndrwxr-x---.  2 polkitd root   28 Jun 29 17:19 redis\ndrwxr-x---.  2 core    core    6 Jun 29 17:16 uploads\n\n```\n\nThe `envs` directory contains all of the environment variable files that configure the following Forem services:\n\n* `forem-imgproxy.service` with `imgproxy.env`\n* `forem-postgresql.service` with `postgresql.env`\n* `forem-rails.service`, `forem-worker.service` with `rails.env`\n* `forem-redis.service` with `redis.env`\n\n```\n# ll envs/\ntotal 12\n-rw-r-----. 1 root root  319 Jun 29 17:16 imgproxy.env\n-rw-r-----. 1 root root   85 Jun 29 17:16 postgresql.env\n-rw-r-----. 1 root root 1483 Jun 29 17:16 rails.env\n-rw-r-----. 1 root root    0 Jun 29 17:16 redis.env\n\n```\n\nIf you have to make a configuration to a service, you can edit the respective ENV file and restart the service via systemd. For example, `systemctl restart forem.service` after editing the `rails.env` file.\n\nThe `version` file is written out by the `forem-container.service` systemd unit and `foremimg` script.\n\n_Note: Making changes to these files can prevent your Forem from starting and cause downtime. Make changes with care and create backups!_\n\n\n## SSH Examples\n\nAll of these examples need to be run via SSH on the Fedora CoreOS server as the `core` or `root` user. You can access your Forem server via SSH:\n\n```\nssh core@\u003cSERVER IP ADDRESS\u003e\n```\n\nIf your key doesn't have a default name like `id_dsa`, `id_ecdsa`, `id_rsa`, etc. you must specify it with the `-i identity_file` option. For example, this is the command you need to run for a key named `forem`:\n\n```\nssh -i ~/.ssh/forem core@\u003cSERVER IP ADDRESS\u003e\n```\n\n### foremctl\n\nWe have a helper script (Forem Control) called `foremctl`. It is used to control your Forem via CLI.\n\n```\n$ foremctl help\n\nUsage: foremctl {console|deploy|help|rake|restart|start|stat|status|stop|update|version}\n\nconsole         Open a Rails console\ndeploy          Updates and deploy the most current version of Forem\nhelp            Show this message\nrake            Run a rake task\nrestart         Restart Forem\nstart           Start Forem\nstats           Show CPU, RAM, Disk IO usage of the Forem containers\nstatus          Show the current running Forem containers\nstop            Stop Forem\nupdate          Updates Forem to the lastest container\nversion         Shows information on the current running version of Forem\n```\n\n### Update Forem to the latest version and restart\n\n```\nsudo foremctl deploy\n```\n\n_Note: The deploy process causes a small amount of downtime while the Forem code restarts._\n\n\n### foremimg\n\nWe have a helper script (Forem Image) called `foremimg`. It is used to control your Forem's version and apply updates.  It has to be run as the `root` user.\n\n```\n# foremimg help\n\nUsage: foremimg {help|rollback|show}\n\nhelp            Show this message\nrollback        Issue a rollback\n\nshow            Show tags: current|rollback\n  current       Show what image is tagged with current\n  rollback      Show what image is tagged with rollback\n\nRunning foremimg with no flags will read /opt/forem/version if present for the\ncontainer tag or write /opt/forem/version with the default tag quay.io/forem/forem:latest\n\nRunning foremimg quay.io/forem/forem:testing will write quay.io/forem/forem:testing\nto /opt/forem/version and pull this container and point the local container\nimage tag 'localhost/forem/forem:current' to 'quay.io/forem/forem:testing'\nand point the previous image to 'localhost/forem/forem:rollback'\n\nRollbacks:\nRunning 'foremimg rollback' will swap 'localhost/forem/forem:rollback' with 'localhost/forem/forem:current'\n\n```\n\n### Set the Forem container repository and tag\n\n```\nsudo foremimg quay.io/forem/forem:testing\n\n```\n\n\n### Update Forem to the latest version with no restart\n\n```\nsudo foremimg update\n\n```\n\n### Rollback Forem to the last running version and restart\n\n```\nsudo foremimg rollback\nsudo foremctl restart\n\n```\n\n### Update Fedora CoreOS to the latest stable version\n\nCheck for updates:\n\n```\n$ sudo rpm-ostree upgrade --check\n2 metadata, 0 content objects fetched; 16 KiB transferred in 1 seconds; 0 bytes content written\nAvailableUpdate:\n        Version: 34.20210529.1.0 (2021-06-01T19:22:39Z)\n         Commit: 936a0a142a09ebf8fa25d50a93377d8822c4ab3bfcf477a73781823569dbd33f\n   GPGSignature: Valid signature by 8C5BA6990BDB26E19F2A1A801161AE6945719A39\n           Diff: 380 upgraded, 22 removed, 17 added\n```\n\nPreview the package updates:\n\n```\n$ sudo rpm-ostree upgrade --preview\n```\n\nDownload the packages without deploying them:\n\n```\n$ sudo rpm-ostree upgrade --download-only\n```\n\nTo apply the updates and reboot:\n\n```\n$ sudo rpm-ostree upgrade\n$ systemctl reboot\n```\n_Note: Fedora CoreOS is an immutable Linux distribution. You will have to reboot to have the updates take effect. This will cause downtime for your Forem._\n\nIf the update causes issues with your Forem, you can issue a rollback with:\n\n```\nsudo rpm-ostree rollback --reboot\n```\n\n### Backup your Forem data\n\nYou can make a backup of your Forem data by creating gzipped tarball of `/opt/forem`. You will want to download this file to your local computer via SCP.\n\n```\nforemctl stop\nsudo tar czpf ~core/\"$(date '+%Y-%m-%d')-forem-data.tar.gz\" /opt/forem\nforemctl start\n```\n_Note: Running `foremctl stop` will cause downtime for your Forem!_\n\n----\n## Development\n\nTo support local development, you will need a Linux workstation with virtualization\nenabled in the BIOS and KVM installed. You can check your workstation to see if it has\nsupport for Intel VT/AMD-V Virtualization with this command:\n\n```bash\ngrep --color \"svm\\|vmx\" /proc/cpuinfo\n```\n\nIf that doesn't return anything, you might need to enable virtualization support\nin your BIOS.\n\n### Install Dependencies\n\nIn order to set up your development VM, you'll need a Linux workstation or server\nwith the following packages:\n\n- Git\n- [Python 3.x](https://www.python.org/downloads/) and pip3\n- Ansible 2.10 or greater (`pip3 install -r requirements.txt`)\n- [Butane](https://github.com/coreos/butane/blob/master/docs/getting-started.md#getting-butane)\n- [libvirt](https://libvirt.org/docs.html)\n- [QEMU](https://qemu-project.gitlab.io/qemu/)\n- `virt-install`\n- [podman](https://podman.io/getting-started/installation)\n\n**Fedora**\n\n```bash\n$ sudo dnf install -y @virtualization butane podman\n```\n\n**RHEL/CentOS**\n\n```bash\n$ sudo yum install -y qemu-kvm virt-install virt-manager podman\n```\n\n**Ubuntu**\n\n```bash\n$ sudo apt install virt-manager\n```\n\n**Debian**\n\n```bash\n$ sudo apt install qemu-kvm libvirt-bin\n```\n\n#### SSH Key\n\nBefore creating your development VM, you must supply an SSH key. SSH is the\nprimary method you'll use to interact with the development VM. Ansible will look\nfor a public key at the following path `/${HOME}/.ssh/forem.pub`.\nCreating a symbolic link to your SSH key is a good way to handle this:\n\n```bash\nln -s \u003cpath_to_your_private_ssh_key\u003e ${HOME}/.ssh/forem.pub\n```\n\n#### SSH Config\n\nAdd this to your `~/.ssh/config` file:\n\n```\nHost devel.forem.wtf\n  ForwardAgent yes\n  StrictHostKeyChecking no\n  UserKnownHostsFile=/dev/null\n```\n\n#### Ansible Vault\n\nIn order to encrypt your variables, you need an Ansible Vault password. This\npassword needs to be set once and never changed. If you lose this password or\nchange this password you will need to reset all of your encrypted variables.\n\n```bash\nmkdir -p ~/.config/forem/\npwgen -1 35 | tee ~/.config/forem/selfhost_ansible_vault_password\n```\n\n#### Create Forem Ansible Inventory\n\n1) Copy example Ansible Inventory from `inventory/example/setup.yml` to `inventory/forem/setup.yml`\n2) Edit `inventory/forem/setup.yml` Ansible Inventory with your Forem settings\n    - Edit the following Ansible Inventory variables:\n        - default_email\n        - forem_domain_name\n        - forem_subdomain_name\n        - forem_server_hostname\n    - Generate and save Ansible Inventory secrets:\n        - vault_secret_key_base\n        - vault_imgproxy_key\n        - vault_imgproxy_salt\n        - vault_forem_postgres_password\n\n#### Launch Forem Locally\n\n```bash\nansible-playbook -i inventory/forem/setup.yml playbooks/providers/qemu.yml\n```\n\n#### Local SSH Access\n\n```bash\nssh core@devel.forem.wtf -p 2222\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fforem%2Fselfhost","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fforem%2Fselfhost","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fforem%2Fselfhost/lists"}