Developing Discourse using a Dev Container

Dev Containers is an open standard for configuring a development environment inside a container. This almost entirely eliminates the need to install/configure Discourse-specific tools/dependencies on your local machine, and makes it very easy to keep up-to-date as Discourse evolves over time.

Dev Containers can be used in a number of different IDEs, or directly using their reference CLI. This guide will describe the setup process for VSCode.

Getting started

  1. Download and install VSCode

  2. Install the Dev Containers extension in VSCode

  3. Clone the Discourse repository onto your machine

    git clone https://github.com/discourse/discourse
    
  4. In VSCode, use File → Open Folder, then choose the Discourse directory

  5. Open the folder in its Dev Container. This can be done via the popup prompt, or by opening the command palette (Cmd/Ctrl + Shift + P) and searching for “Open folder in container
”

  6. If this is your first time launching a container, you will be prompted to install and start Docker Desktop. Once complete, go back to VSCode re-run “Open folder in container
”

  7. Wait for the container to download and start. When it’s done, the README will appear, and you’ll see the Discourse filesystem in the sidebar.

  8. Run the default build task using Cmd/Ctrl + Shift + B.

    This will install dependencies, migrate the database, and start the server. It’ll take a few minutes, especially on the lower-end machines. You’ll see “Build successful” in the terminal when it’s done.

  9. Run the dev/admin/create task. Open the command palette and search for “Tasks: Run Tasks”. It will present a menu of tasks; select dev/admin/create off of that list. You’ll be prompted to enter an email address and a password for your admin user.

  10. Visit http://localhost:3000 in your browser to see your new Discourse instance

  11. All done! You can now make changes to Discourse’s source code and see them reflected in the preview.

Running tests

The first time you run tests, you’ll need to install testing dependencies, including Playwright and the discourse_test DB. You can run the deps/testing task to install those. Open the command palette, select “Tasks: Run Tasks”. It will present a menu of tasks; select deps/testing off of that list.

Once the test dependencies are installed, you can run bin/lint, bin/qunit, or the system specs

Applying config/container updates

Every so often, the devcontainer config and the associated container image will be updated. VSCode should prompt you to “rebuild” to apply the changes. Alternatively, you can run “Dev Containers: Rebuild Container” from the VSCode command palette. The working directory, and your Redis/Postgres data will be preserved across rebuilds.

If you’d like to start from scratch with fresh database, you’ll need to delete the discourse-pg and discourse-redis docker volumes. This can be done from the “Remote Explorer” tab of the VSCode sidebar.

Discourse’s sample vscode .vscode/settings.json and .vscode/tasks.json will be copied when you first boot the codespace. From that point forward, if you want to use the latest sample config, you’ll need to manually copy .vscode/settings.json.sample to .vscode/settings.json.

References


This document is version controlled - suggest changes on github.

13 Likes

Hi,

No admin account created

When using the docker container from outside via the scripts in d/ (e.g. d/boot_dev --init as specified in Install Discourse for development using Docker , it asks me to set up an admin account as part of the process.

However, when using it as a Dev Container and running the build steps (ctrl/cmd + shift + b) it does NOT create an admin.

From glancing at the instructions, I first had the impression that creating an admin is quite arduous; but then I realized that all it takes is this command, leaving it here for those who run into the same issue:

rake admin:create

(or, if it complains about a different rake version required: bundle exec rake admin:create)

7 Likes

On Windows 11, if you don’t want to run into line ending issues, such as:

[23963 ms] Start: Run in container: /bin/sh -c ./.devcontainer/scripts/start.rb
/usr/bin/env: ‘ruby\r’: No such file or directory
/usr/bin/env: use -[v]S to pass options in shebang lines


 make sure to Clone in Volume

6 Likes

Maybe there is a better way, but to work on plugins I have a discourse-plugins sibling folder alongside my main discourse repo folder. This mounts to /workspace/plugins so I can then create symlinks within the container.

This is what I added to mounts in devcontainer.json:
"source=${localWorkspaceFolder}/../${localWorkspaceFolderBasename}-plugins,target=/workspace/plugins,type=bind"

2 Likes

This is indeed useful, thank you.

1 Like


 Or just git reset --hard
Worked for me
Then Dev Container: Rebuild Container and Ctrl-Shift-B

If you are using OrbStack (not affiliated) on your local macOS environment, and want to run Discourse on HTTPS with a custom domain, update your devcontainer.json with following additions:

  1. Give a name to the container.
  2. Add .orb.local wildcard domain to RAILS_DEVELOPMENT_HOSTS environment variable (hostnames must be separated by a comma).
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -13,10 +13,11 @@
   ],
   "remoteUser": "discourse",
   "remoteEnv": {
-    "RAILS_DEVELOPMENT_HOSTS": ".app.github.dev",
+    "RAILS_DEVELOPMENT_HOSTS": ".app.github.dev,.orb.local", // Step 2
     "PGUSER": "discourse",
     "SELENIUM_FORWARD_DEVTOOLS_TO_PORT": "9229",
   },
+  "runArgs": ["--name","discourse"], // Step 1
   "mounts": [
     "source=${localWorkspaceFolderBasename}-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume",
     "source=${localWorkspaceFolderBasename}-pg,target=/shared/postgres_data,type=volume",

p.s. please let me know, if you know how I can set the *.orb.local hostname and the container name dynamically, as it is defined for GitHub Codespaces. Setting the value as .app.github.dev,.orb.local didn’t work for me.

Update: Somehow, I was missing a record in my /etc/hosts file. After adding this line, I was able to use .orb.local wildcard domain in step 2.

With these changes in the devcontainer.json file, now I can run my local Discourse instance at https://discourse.orb.local/

/etc/hosts

Add this line to your /etc/hosts file if you don’t already have it.

##
# Docker and OrbStack
##
127.0.0.1 host.docker.internal

Bonus tip 1
If somehow your network settings, or your company VPN network, etc conflicts with OrbStack’s Container IP ranges, update your OrbStack with a different range.

Bonus tip 2
If you omit the step 1, OrbStack will create a randomly named container, but you will still be able use HTTPS without appending any port number. Downside is the container name, thus the domain name will be refreshed every time you rebuild the container.

1 Like

It seems that Orbstack is to run Docker images. If you’re trying to run production, maybe you’d like to use:

If it’s for development, I think Discourse uses the discourse/discourse_dev - Docker Image image. But you’ll likely have to do your own setup.

FYI, I believe the Docker

command’s code is in discourse/bin/docker/boot_dev at main · discourse/discourse · GitHub.

1 Like

For those who are not married to vscode, this is the process to do devcontainers with just the devcontainers cli.
Assuming you have devcontainers installed already:

Build Container

git clone https://github.com/discourse/discourse && cd discourse
devcontainer build
devcontainer up --workspace-folder .
devcontainer exec bash

Once inside the container you need to build the deps

pnpm install
bundle install
SKIP_MULTISITE=1 SKIP_TEST_DATABASE=1 bin/rake db:create db:migrate
DISCOURSE_DEV_ALLOW_ANON_TO_IMPERSONATE=1 bin/ember-cli -u > /dev/null 2>&1 &
  • Change /dev/null to a different file if you want logs
  • If you want to leave it running while disconnected from the shell run disown

Access discourse

docker inspect <name> | jq '.[0].NetworkSettings.Networks.bridge.IPAddress'

This will reveal the ipaddress assigned to the container.
In your browser open http://<ipaddress>:4200

Cleanup

To delete your devcontainer (the down/delete options are not developed)
Retrieve the name of the container
docker ps
stop and delete the container:
docker stop <name> && docker rm <name>
delete the volumes:
docker volume rm discourse-node_modules discourse-pg discourse-redis

4 Likes

Those non-VS Code instructions didn’t work for me just now on macOS. I recommend that macOS users who want to interact with the Discourse Docker image outside of VS Code use the legacy boot_dev Docker script instead.

With docker ps I found my container name (a randomized silly name, like peaceful_lumiere). I ran docker inspect peaceful_lumiere | jq '.[0].NetworkSettings.Networks.bridge.IPAddress' and it spit out an IP address. I went to http://<ip>:4200 in my browser, and it just sat there spinning forever.

I think that’s because the ember-cli dev server isn’t listening on all interfaces; it’s only listening on http://127.0.0.1:4200 inside the container.

I eventually got it to work by adding a runArgs section to .devcontainer/devcontainer.json, like this.

     8025, // mailhog
     9229  // chrome remote debug
   ],
+  "runArgs": [
+    "-p",
+    "127.0.0.1:4200:4200",
+    "-p",
+    "127.0.0.1:3000:3000",
+    "-p",
+    "127.0.0.1:9292:9292",
+    "-p",
+    "127.0.0.1:8025:8025",
+    "-p",
+    "127.0.0.1:9229:9229"
+  ],
   "remoteUser": "discourse",
   "remoteEnv": {
     "RAILS_DEVELOPMENT_HOSTS": ".app.github.dev",


 but if you do that, it won’t work in VS Code (because VS Code and the devcontainer will both try to forward ports). I made a separate devcontainer-cli.json and used devcontainer --override-config .devcontainer/devcontainer-cli.json, and that worked.

But then it dawned on me: I’ve jumped through all these hoops, but I’m no better off than using the existing legacy boot_dev script. Following those documented steps is quicker and easier than trying to coerce devcontainer to do the right thing from the CLI.

Dev Containers are primarily intended for use in VS Code, or something that will automatically manage port forwarding for you; devcontainer CLI doesn’t do that. So, if you don’t want VS Code, maybe don’t bother with Dev Containers.

I should mention my instructions were for Linux. (NixOS specifically in my case) but no reason it shouldn’t work in other Linux based systems.

@dfabulich I read your post and I think I understand the issue. Port forwarding shouldn’t be necessary. You need the containers IP. 127.0.0.1 is localhost (your local system, not the container)

The jq command should have output a local lan subnet IP you can access without port forwarding.

No, I did that. jq printed out "172.17.0.2" and http://172.17.0.2:4200 just sat there spinning forever.

Inside the container, ember-cli isn’t listening on 0.0.0.0:4200; it’s listening on 127.0.0.1:4200. So if you access it at 172.17.0.2:4200 (or whatever your actual IP is), it never gets through to ember, at least on macOS.

Note that on macOS, Docker Desktop is just running a virtual machine. There’s no such thing as a “lightweight process-level Docker container” on macOS.

I don’t know enough about macos to know if docker uses kernel namespaces like it does on linux. I just gave it a test again (Note: it does take a bit for the service to start up):


It doesn’t. Docker Desktop for macOS is literally running QEMU. (Or, it was up until recently; now they’re using a new thing that’s basically like QEMU wearing a funny hat and mustache; either way, it’s still running a full-fat VM as its implementation.)

So, I’m sure it works on your machine, but it doesn’t work on my macOS machine, and I think that’s the most likely reason why.

1 Like