Install plugins on a self-hosted site

:warning: This guide assumes that you have a self-hosted standard installation. We only support the standard method of install here, so these instructions assume you have a standard install.

:warning: This guide only applies to self-hosted Discourse instances. If you are using a managed hosting service, the available plugins are controlled by your hosting provider. For example, on our hosting these specific plugins are available by hosting tier.

In this tutorial, we’ll install the Discourse Solved plugin.

This tutorial requires an understanding of how to use GitHub repos, specifically, how to get or copy the git clone URL and how to edit YAML (*.yml) files via the terminal using nano, specifically how to save and exit on nano. YAML files strongly follow indentations, so be sure to respect these as you copy and paste and edit the necessary *.yml for your Discourse instance.

  • Copy the plugin’s GitHub git clone url.

  • Access your container’s app.yml file (present in /var/discourse/containers/)

    cd /var/discourse
    nano containers/app.yml
    
  • Add the plugin’s repository URL to your container’s app.yml file:

    hooks:
      after_code:
        - exec:
            cd: $home/plugins
            cmd:
              - sudo -E -u discourse git clone https://github.com/discourse/docker_manager.git
              - sudo -E -u discourse git clone https://github.com/discourse/discourse-solved.git
    

    Add the plugin’s git clone url just below the line containing git clone https://github.com/discourse/docker_manager.git)

    Follow the existing format of the docker_manager.git line; if it does not contain sudo -E -u discourse then insert - git clone https://github.com/discourse/discourse-solved.git.

  • Rebuild the container:

    cd /var/discourse
    ./launcher rebuild app
    

    That’s it, you’ve successfully installed the Discourse Solved plugin on your Discourse instance!


If your plugin is hosted in a private repository

You must use an OAuth token:

With the OAuth token, you can install your plugin in the same way as a public repo, and you don’t need to create a SSH key.

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
              - sudo -E -u discourse git clone https://<token>@github.com/owner/repo.git

We strongly advise you to use OAuth tokens for plugins in private repositories. However, if you cannot, see below.

SSH Key Private Install Steps
run:
  - exec: echo "Beginning of custom commands"
  
  - exec: cd /var/www/discourse && sudo -u discourse bundle install --deployment --without test --without development
  - exec: echo "Host github.com\n\tStrictHostKeyChecking no\n" >> /user/.ssh/config
  - file:
      path: /user/.ssh/id_rsa
      chmod: 600
      contents: |
        -----BEGIN RSA PRIVATE KEY-----
        MIIEogIBAAKCAQEArCQG213utzqE5YVjTVF5exGRCkE9OuM7LCp/FOuPdoHrFUXk
           .... etc ....
        -----END RSA PRIVATE KEY-----
  - file:
      path: /user/.ssh/id_rsa.pub
      chmod: 600
      contents: ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tj .... etc .... user@discourse
  - exec: cd $home/plugins && git clone git@github.com:SecretOrg/secret-plugin.git
  - exec: cd $home && sudo -E -u discourse bundle exec rake db:migrate
  - exec: cd $home && rm -fr tmp/cache
  - exec: cd $home &&  sudo -E -u discourse bundle exec rake assets:precompile
  - exec: rm /user/.ssh/id_rsa
  - exec: rm /user/.ssh/id_rsa.pub

  - exec: echo "End of custom commands"
  • Rebuild the container:
cd /var/discourse
./launcher rebuild app

Your private plugin should be installed on your Discourse instance.

How to uninstall a plugin

To remove a plugin, simply remove the - git clone https://github.com/... line from your app.yml file and rebuild your site via

cd /var/discourse
./launcher rebuild app
167 Likes

I updated the guide to use another example plugin instead of Discourse Spoiler Alert, which has been bundled into core.

I used Discourse Solved, but let me know if a different one would be better.

7 Likes

I hosted discourse on Digital Ocean and when i used that format it showed me this error.

FAILED
--------------------
Pups::ExecError: cd /var/www/discourse/plugins && sudo -E -u discourse git clone https://github.com/discourse/docker_manager.git  - sudo -E -u discourse git clone https://github.com/discourse/discourse-solved.git failed with return #<Process::Status: pid 252 exit 129>
Location of failure: /usr/local/lib/ruby/gems/3.2.0/gems/pups-1.2.1/lib/pups/exec_command.rb:132:in `spawn'
exec failed with the params {"cd"=>"$home/plugins", "cmd"=>["sudo -E -u discourse git clone https://github.com/discourse/docker_manager.git  - sudo -E -u discourse git clone https://github.com/discourse/discourse-solved.git"]}
bootstrap failed with exit code 129
** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one.
./discourse-doctor may help diagnose the problem.
2a49220a4ff35feac3c1a9a4a9c1cad1094bde4c9ba321975ebc9ae7ead2bbee

Then i used this format

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - sudo -E -u discourse git clone https://github.com/discourse/docker_manager.git && sudo -E -u discourse git clone https://github.com/discourse/discourse-solved.git

instead of

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - sudo -E -u discourse git clone https://github.com/discourse/docker_manager.git
          - sudo -E -u discourse git clone https://github.com/discourse/discourse-solved.git
3 Likes

8 posts were split to a new topic: Formatting issues adding plugins to app.yml

sorry for the dumb question but, how should I know if its required to include sudo -E -u discourse or not for the git clone statement?

2 Likes

How does your docker_manager line look like?

3 Likes

none of the other entries in my app.yml had the sudo part so I omitted that and its just worked so far

4 Likes
- exec: echo "Host github.com\n\tStrictHostKeyChecking no\n" >> /user/.ssh/config

Seems like this method is not working anymore.

sh: 1: cannot create /user/.ssh/config: Directory nonexistent

With my self-hosted GIT repository, I’d really like to use SSH keys.

1 Like

You shouldn’t need an ssh key in the container, just delete this:

1 Like

Sorry, I don’t think I follow…

My repo is accessible via ssh. How can I get a plugin cloned without SSH key?

1 Like

Oh. Maybe I’m wrong. Then maybe you need to create that directory with the right permissions m

1 Like

What is confusing is that part of the commands in examples are to be run as root and part as user discourse. It doesn’t seem to be very consisent.

  - exec: cd $home/plugins && git clone git@github.com:SecretOrg/secret-plugin.git

As opposed to

  - exec: cd $home && sudo -E -u discourse bundle exec rake db:migrate
 

I will do some digging in the running container’s shell.

This plugin installation is a bit complicated. It would be great if it could be made the same as installing theme.

1 Like

Dashboard.literatecomputing.com will install plugins with a few clicks. You can use it for free by joining the free trial group.

3 Likes

@Discourse, If you want to update the guide, here is the working code:

To use your own SSH keys for accessing a private repository, follow these steps:

  1. Place the private SSH key in /var/discourse/shared/standalone/identity/id_rsa (or another location accessible by the container; if you choose a different location, remember to update the path in the template below accordingly).

  2. Use the following template to add your plugins:

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          # Set private repo access
          - chown $(whoami) /shared/identity/id_rsa && chmod 600 /shared/identity/id_rsa
          - git config --global core.sshCommand "ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i /shared/identity/id_rsa"

          # My private plugins
          - git clone git@example.com:repo/private-discourse-plugin.git

          # Unset access
          - git config --global --unset core.sshCommand
1 Like

Thanks for something I don’t need that one but I live with simple original.

Do you want to explain why we should use your example? Is it because of this:

use your own SSH keys for accessing a private repository

Why not just use a token in the URL?

Well, we have tools to solve out this riddle :stuck_out_tongue_winking_eye:

Understanding the Usefulness of Container Configuration Snippets AI

Jagster:

Why someone would like to use that snippet in container/app.ym

gpt-4o:

To effectively answer your question, I need to understand the context around the specific snippet in container/app.yml. Could you please provide more details or the actual snippet you’re referring to? This way, I can give you a precise and helpful explanation.

Jagster:

Sorry, but I needed this composer.

This is the snippet:

”””

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          # Set private repo access
          - chown $(whoami) /shared/identity/id_rsa && chmod 600 /shared/identity/id_rsa
          - git config --global core.sshCommand "ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i /shared/identity/id_rsa"

          # My private plugins
          - git clone git@example.com:repo/private-discourse-plugin.git

          # Unset access
          - git config --global --unset core.sshCommand

”””

gpt-4o:

Thank you for providing the snippet. This YAML snippet is intended to be included in the container/app.yml configuration file for a Discourse instance. Let’s break down what it does:

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          # Set private repo access
          - chown $(whoami) /shared/identity/id_rsa && chmod 600 /shared/identity/id_rsa
          - git config --global core.sshCommand "ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i /shared/identity/id_rsa"

          # My private plugins
          - git clone git@example.com:repo/private-discourse-plugin.git

          # Unset access
          - git config --global --unset core.sshCommand

Explanation

  1. Setting Directory:

    cd: $home/plugins
    

    This changes the working directory to where plugins are located ($home/plugins).

  2. Setting Private Repository Access:

    - chown $(whoami) /shared/identity/id_rsa && chmod 600 /shared/identity/id_rsa
    - git config --global core.sshCommand "ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i /shared/identity/id_rsa"
    
    • Changes the ownership of the SSH private key (id_rsa) to the current user and sets its permissions to 600.
    • Configures Git to use a custom SSH command which uses the specified private key and disables strict host key checking.
  3. Cloning Private Repository:

    - git clone git@example.com:repo/private-discourse-plugin.git
    

    Clone a private Git repository containing a Discourse plugin.

  4. Unsetting SSH Command:

    - git config --global --unset core.sshCommand
    

    Resets the Git SSH command configuration to its default state.

When and Why to Use This Snippet

  1. Private Repositories: If you have a private Discourse plugin repository that is not publicly accessible and requires SSH key-based authentication.

  2. Automation: This script helps automate the process of setting up access to the private repository, cloning it, and ensuring the environment is reset afterwards. This is useful for continuous deployment setups or automated server provisioning.

  3. Security Management: It handles the SSH keys securely (setting file permissions and then unsetting the custom SSH command), critical for maintaining security posture during automated deployments.

Important Considerations

  • Ensure your private SSH key (id_rsa) is securely managed and not exposed.
  • Verify the ownership and permissions are correctly set for your security policies.

For more detailed information and contexts about app.yml and hooks in Discourse, you can check relevant Discourse documentation.

If you have any further questions or need additional details on the configuration, feel free to ask!

1 Like