Heroku CLI’s heroku run and heroku run:detached commands: A Quick Guide

Heroku

The Heroku CLI contains a lot of powerful CLI commands. Today we will dive into 2 of them: the heroku run and heroku run:detached commands. Both allow you to run a one-off process inside a heroku one-off dyno right from the terminal, but how are they different and to what use cases does each apply?

What are one-off dynos?

Before getting into the Heroku CLI commands, let's make sure we understand what Heroku's one-off dynos are and when to use them.

In more traditional hosting environments, developers would often log in to servers to perform certain actions. With Heroku, this can be achieved by launching one-off dynos.

As Heroku's official article states:

"When you wish to do one-off administrative or maintenance tasks for the app, or execute some task periodically using Heroku Scheduler, you can spin up a one-off dyno. ... One-off dynos run alongside other dynos, exactly like the app’s web, worker, and other formation dynos."

In other words, one-off dynos are dynos that run a short-lived command and then shut down after the command has finished running. These one-off dynos contain your application code, but do not serve web requests. They are often used to run database migrations or to execute one-time scripts committed into the Heroku app’s repo.

You can start a one-off dyno in many ways. For example, by using the Dyno Create endpoint of the Heroku Platform API directly or through an Heroku add-on such as Advanced Scheduler. Today we do it using the Heroku CLI.

Heroku run

Now we know exactly what a one-off dyno is, let's start one using the heroku run command:

$ heroku run “echo ‘Hello World’” -a rocky-cliffs-33279
Running echo 'Hello World' on ⬢ rocky-cliffs-33279... up, run.8495 (Standard-1X)
Hello World

This example above starts a one-off dyno executing the echo command. The echo command logs ‘’Hello World”, after which it exits.

Heroku run creates a one-off dyno and attaches it to your terminal, with a character-by-character TCP connection for STDIN and STDOUT. All output gets streamed to the terminal. The connection will remain open until the one-off dyno terminates.

This allows the heroku run command to be used for commands that exit on their own, as well as for interactive processes such as bash:

$ heroku run bash -a rocky-cliffs-33279
Running bash on ⬢ rocky-cliffs-33279... up, run.3727 (Standard-1X)
~ $ 

This particular command starts a bash process and allows you run additional commands directly in the one-off dyno. One-off dynos created using heroku run have a timeout of one hour. After that time of inactivity in both input and output, connection to the dyno is closed and the dyno terminates. Press Ctrl+C to terminate the one-off dyno and close the connection yourself.

Since all output is streamed to your terminal, the only thing recorded in your Heroku app’s logs is the startup and shutdown of the dyno:

app[api]: Starting process with command `echo 'Hello World'` by user xxx
heroku[run.7847]: Awaiting client
heroku[run.7847]: State changed from starting to up
heroku[run.7847]: Starting process with command `echo 'Hello World'`
heroku[run.7847]: Process exited with status 0
heroku[run.7847]: State changed from up to complete

As you can see, "Hello World" does not get logged in your Heroku app's logs.

The heroku run command is very useful when you need to manually run a one-off script and you are interested in its output.

Heroku run:detached

What if you do not need to run an interactive process and just want a one-off dyno to perform a certain task? This is where the heroku run:detached command comes in.

Let's run the echo command again, using the heroku run:detached command instead:

$ heroku run:detached "echo 'Hello World'" -a rocky-cliffs-33279
Running echo 'Hello World' on ⬢ rocky-cliffs-33279... done, run.2124 (Standard-1X)
Run heroku logs --app rocky-cliffs-33279 --dyno run.2124 to view the output.

You can immediately see that this Heroku CLI command behaves differently. Heroku run:detached starts a detached one-off dyno. This one-off dyno does not get attached to your terminal, but runs independently in the background. As a result, output is not streamed to the terminal, but is sent to your Heroku app's logs. These logs can be retrieved using the suggested heroku logs command:

$ heroku logs --app rocky-cliffs-33279 --dyno run.2124
heroku[run.3589]: Starting process with command `echo 'Hello World'`
heroku[run.3589]: State changed from starting to up
app[run.3589]: Hello World
heroku[run.3589]: Process exited with status 0
heroku[run.3589]: State changed from up to complete

In contrast to the heroku run command, "Hello World" does get logged in your Heroku app's logs.

While attached one-off dynos started through heroku run have a timeout of one hour, detached dynos started through heroku run:detached have no timeout. However, like all dynos, one-off dynos are cycled every 24 hours. As a result, a one-off dyno will run for a maximum of 24 hours.

Detached one-off dynos are ideal for running tasks in the background. A popular use case is having Heroku Scheduler start them on a scheduled interval to execute one-off tasks.

Next steps

Clearly, there are some key differences between the heroku run and heroku run:detached commands, making each ideal for specific use cases.

Now you know how and when to use both Heroku CLI's command, try creating your own script and run it in your terminal, or schedule it to be executed by one of Heroku's scheduler add-ons!