Deployment
Brut apps are Rack apps, so they can be deployed in conventional ways.
Overview
There are just too many ways to deploy. Brut attempts to address this by adhering to 12-factor principles. You can get your app to production and run bin/run and it should work.
That said, Brut currently provides explicit support for two methods of deployment: Heroku (using containers) and a generalized Docker Image mechanism.
General Design of Deployment
Brut attempts to consolidate all production deployment configuration in deploy/deploy_config.rb, which is expected to define the class AppDeployConfig, which is expected to extend Brut::CLI::Apps::Deploy::DeployConfig. Brut will create an instance of this class and use it to understand production aspects of your deployment. No YAML.
At its most basic, in production you will need to know what long-running processes to run. By default, you'd run a web process, but you may also run Sidekiq or other services that need access to your app's source code.
The most full-featured method currently supported is Heroku, using containers.
Heroku Container-based Deployment
When creating your Brut app with brut new, the Heroku segment can be used to create files for a Heroku container-based deployment.
If you already created your app, you can add this segement via:
brut new segment herokuHow to deploy:
Get an auth token from Heroku, which you can do from inside the container, and save it to
bash_customizations.local:your-computer> dx/exec bash devcontainer> heroku auth:login # You will need to copy/paste the URL to log in devcontainer> heroku authorizations:create -d "container pushes" --expires-in 31536000 # Copy the token output by this command devcontainer> echo "HEROKU_API_KEY=«TOKEN YOU COPIED»" >> dx/bash_customizations.localExit the devcontainer and stop
dx/start(e.g. hitCtrl-Cwherever you ran it)Rebuild and restart the devcontainer (this will set
HEROKU_API_KEYfor you)your-computer> dx/build your-computer> dx/start # In another terminal window your-computer> dx/exec bash devcontainer> echo $HEROKU_API_KEY # You should see the tokenSetting this environment variable avoids having to constantly re-authenticate to Heroku.
Create your app using the container stack:
> heroku create --stack container -a «your heroku app name»Ensure your app's source code is all checked in, there are no uncommitted or unadded files, and you have pushed to the
mainbranch of your remote Git repository.brut deploy herokuThis will generate a
Dockerfilefor each process (by default,Dockerfile.webandDockerfile.release), build images, push those images to Heroku, and ask Heroku to release them.
Debugging Tips:
- Keep in mind it's hard to make general deployment tools. You are expected to understand your deployment and be capable of deploying an arbitrary Rack app manually. Brut's tooling automates what you need to do based on what you already need to know.
- Try building images first:
brut deploy heroku --build-only - It's possible to run the images locally, but you will ned to run Postgres at the very least.
General Docker Image Deployment
When creating your Brut app with brut new, the DockerDeploy segment can be used to create files for a Heroku container-based deployment.
If you already created your app, you can add this segement via:
brut new segment docker-deployThis segment provides a more generic method of deployment using Docker images and Docker registries. Under this method, you author a Dockerfile which is then used to build an image whose name is based on your app's org and id (as defined in your subclass of Brut::Framework::App), as well as the current SHA-1 of your git repo. This image is then pushed to a registry of your choice. After that push, you are on your own to pull it down somewhere.
brut deploy dockerYou can do brut deploy docker --no-push to build only.
If you aren't using DockerHub, you can set your registry in deploy/deploy_config.rb:
class AppDeployConfig < Brut::CLI::Apps::Deploy::DeployConfig
def registry_hostname = "ghcr.io"
endManagement of a docker-compose.yml
If you want to use Docker Compose in production, Brut can assist with managing the docker-compose.yml file.
brut deploy docker_compose generateThis will create a deploy/docker-compose.yml if one is not there. It will have default settings that have worked for me, but you should review this file and make sure it's correct.
If the file is there, Brut will make it consistent with your deploy/deploy_config.rb file:
- Any service in
docker-compose.ymlthat is not part of your config is removed - Any service not in
docker-compose.ymlwill be added - Services in both
docker-compose.ymland your config will be updated indocker-compose.ymlas follows:command:will be set to the command in your configimage:will be set toREGISTRY/ORG/APP_ID:${DOCKER_IMAGE_TAG}, whereREGISTRYis the value from your deploy config andORGandAPP_IDare from yourAppclass. Your production environment is expected to setDOCKER_IMAGE_TAGin the UNIX environment. This is because Brut'sbrut deploy dockerimplementation does not uselatest, sincelatestin Docker-land is absolutely cursed.
Instead of changing the file, you can check its consistency first:
brut deploy docker_compose checkThis will output differences between what generate would do and what exists.
NOTE
Brut assumes your deploy/docker-compose.yml is to be checked in, so if you use brut deploy docker_compose generate to update it, you must commit and push that change to use brut deploy docker.
Other Mechanisms for Deployment
As a Rack app, other deployments should be possible. To make the app work, you'll need to make sure a few things are dealt with:
RACK_ENVmust be"production"brut build-assetswill build all assets by default. This must either be done on production servers or done ahead of time and the results packaged with the app.brut build-assetsoutputs files inapp/publicandapp/config. Those files are used at runtime. Brut will not initiate the build of any assets.- If you are going to build assets on production servers, you must included developer tooling. This means NodeJS, all modules in
package.jsonand all RubyGems inGemfile.
Testing
Testing deployments is a bit out of scope, but in general:
- A container-based deployment can theoretically be run on your computer as a test.
- Non-production, but production-like environments can be used to validate production configurations.
- You own the means of production…not Brut.
Recommended Practices
- Avoid a lot of code that checks
Brut.container.project_env. Try to consolidate all prod/test/dev differences in environment variables. - Have a way to get a shell into your production environment for debugging.
- Brut doesn't log much, but if you remove the
OTEL_*environment variables, Brut will log OTel telemetry to the console, which may be useful. - Setting
OTEL_LOG_LEVEL=debugis advised if the app isn't starting or you aren't seeing any telemetry or logging
Technical Notes
IMPORTANT
Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's internals, the source code is always more correct.
Last Updated July 3, 2025
None at this time.