Heroku Dynos give you the capability to host and run code in the cloud using a git interface. Basically you can define your workflow application in a git repository which is hosted on Heroku (instead of a place like GitHub) but is cloned to your local workstation. After editing your source code in the local clone, to deploy your workflow code to the Heroku web server you do a git commit and git push. There is special post-commit processing in Heroku that will unpack your code from the git server and create and deploy a container to a Heroku Dyno.

Some of the benefits of Heroku Dynos include:

  • Includes TLS (server certificate) automatically and maps to port 443. Heroku appears to be running a reverse proxy running on port 443 on the internet, which automatically includes TLS, and standard port numbers like 443 can be used in the URL to reach it. A hostname of a random name is assigned to you automatically for your external URL. Inside your dyno, your app will be asked to use a specific "internal" port number which is passed to you as an environment variable, so you will need to listen on that port number. But the reverse proxy will connect its external-listening port 443 to the specific port number on your dyno.
  • Git is a fairly easy way to manage your workflow application, especially for providing version control (backups!) and sharing/coordinating with other team members.
  • Heroku does have a free tier, although that has usage limits and hostname expirations.

Some of the challenges of Heroku Dynos include:

  • Container building and deployment can take a while, on the order of 15-30 seconds, each time you make a change to your code and git push. This is not as quick as a text editor "save" and workflow app restart when you ssh directly in to the server or run on your local workstation. You can't ssh directly in to a Heroku dyno.
  • Heroku dynos in the free tier will go to sleep (idle state) after period of inactivity. A subsequent websocket request will cause the dynos to wake up, but the time it takes to wake up (perhaps 15 seconds) can exceed the timeout from the Relay server. You can hit the URL from a web browser to wake up the dyno so it is ready for subsequent workflow requests, but you need to be aware of that. A paid tier can keep your dyno from going to sleep in this idle state.
  • You won't be able to test your code before git push because the server instantiated by heroku local command isn't reachable from the internet, where the Relay server can connect from. So you'll need to git push to your dyno for each local change you want to test.

If you choose to use Heroku, we suggest that you deploy one of their hello-world projects (in the language of your choice) as practice and to get more familiar with Heroku. This includes installing a Heroku CLI locally.

Tips

  • There are certain metadata files that the dyno needs to properly build and run your application. The metadata files will configure things like which language should be used (i.e., Node, python, etc), which language runtime version your want (i.e., Note 16.13.2, python 3.10.4, etc), and the file that contains your code's entry point (i.e., index.js, myworkflow.py, etc). The documentation in the Heroku website does a pretty good job explaining what is needed. Also check the README.md in the language-specific Relay SDK for additional tips.

  • Even though a Relay workflow application uses a websocket, in Heroku you define this application as a web application.

  • Use the IP address 0.0.0.0 instead of localhost as the parameter in the Relay SDK call to instantiate your workflow server.

  • Read the environment variable PORT for the port number that Heroku wants your web app to listen on. And pass that port number into the SDK call to instantiate your workflow server. Remember that Heroku automatically provides a reverse proxy that will answer your request externally on the standard port 443 and then forward that to the local PORT value.

  • Heroku requires that the branch name be "main" or "master", otherwise the push will not get built and deployed there after the git server receives it.

  • Use heroku logs --tail to watch your workflow run, and to see when your dyno goes idle and wakes up.

  • If upon a workflow request you see in the log a 50x error saying "No web processes running" then you need to start the web process with the Heroku CLI:
    $ heroku ps:scale web=1

  • In the git push response, you should see the base URL of your app: remote: https://remarkable-relay-12345.herokuapp.com/ deployed to Heroku. When registering your workflow with the Relay server using the Relay CLI, you'll need to use this hostname (replacing https with wss) and add the workflow's path part to this base URL. You can also see the name of the app using the heroku config or heroku releases CLI commands, in case the git push response has scrolled off your screen: === remarkable-relay-12345 Config Vars

  • From a web browser, hit the URL of your Heroku server with https on port 443 (Heroku adds a reverse proxy automatically to your web app to provide TLS capability and have it be reachable externally on port 443):

        https://remarkable-relay-12345.herokuapp.com/hellopath
    

    and if properly configured, the browser should show a message like this one from Chrome and Firefox:

        Failed to open a WebSocket connection: invalid Connection header: keep-alive.
        You cannot access a WebSocket server directly with a browser. You need a WebSocket client.
    

    In the log, you should see status=101:
    2022-03-30T20:54:25.246876+00:00 heroku[router]: at=info method=GET path="/hellopath" host=remarkable-relay-12345.herokuapp.com request_id=b6e340f3-2c59-4acc-9da5-b12dcf11fb07 fwd="52.1.25.164" dyno=web.1 connect=0ms service=2ms status=101 bytes=203 protocol=https
    If instead in the log you see a 50x error saying "No web processes running" like this:
    2022-03-30T19:36:12.155629+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/" host=remarkable-relay-12345.herokuapp.com request_id=12cc47a5-5d1d-4e18-b6f3-7cef784e56f6 fwd="8.48.63.104" dyno= connect= service= status=503 bytes= protocol=https
    then you need to start the web process with the Heroku CLI:

        $ heroku ps:scale web=1
    
  • Also do a heroku ps CLI command and verify that your dyno is up and not sleeping/idle. The reason for this check is because the dyno can take 4-6 seconds to wake up from sleep, which may hit a
    timeout for the Relay server. For example, compare the "up" case to the "idle" case:

        === web (Free): python mywf.py (1)
        web.1: up 2022/03/30 17:42:09 -0400 (~ 1m ago)
        (versus)
        === web (Free): python mywf.py (1)
        web.1: idle 2022/03/31 14:42:07 -0400 (~ 1m ago)
    

    If your dyno is idle, hit the URL with a web browser to wake it up. It will go idle again in a few minutes of non-use.

  • If you want to manually wake up an idle dyno, you can hit the URL from a standard web browser. Because the web browser is looking for an HTTP response instead of a websocket, it is normal for the web browser to display an error such as You cannot access a WebSocket server directly with a browser. You need a WebSocket client. But even with this error, it should wake up an idle dyno.

We aren't recommending any specific providers, but since Heroku is common we'd like to share our lessons learned to help you get up and running quickly.