Deploying to Heroku: Docker, Go and React
Tags: go, react, heroku, docker
Published: 2019-12-29 12:54:14


We're going to deploy a **React Application** to **Heroku** that will be served from a **Go backend** which is then neatly wrapped in a **Docker image**.

You'll need the following:

- Golang (I am using v1.13.5)
- npm
- A text editor (VSCode is what I am using)
- A unix based terminal
- Docker
- A Heroku account and the Heroku CLI

# What is Heroku?

From their [page](https://www.heroku.com/home)

_"Heroku is a platform as a service (PaaS) that enables developers to build, run, and operate applications entirely in the cloud."_

# Use CRA to set up a React app

We're going to use [create-react-app](https://reactjs.org/docs/create-a-new-react-app.html) to bootstrap our React project.

`npx create-react-app deployment-demo`

# Write the Go backend to serve it

Let's change directory into our newly created react project, create a sub-directory called `server` and create a file called **server.go** in it:

`cd deployment-demo/ && mkdir "server" && cd "$_" && touch server.go`

Open up your favourite text editor and paste the following into **server.go**:

```go
package main

import (
	"log"
	"net/http"
	"os"
)

func main() {
	/*
		Grab the environment variable that has been hopefully set, but also set up a default
	*/
	port := os.Getenv("PORT")
	defaultPort := "8080"
	/*
		Serve the contents of the build directory that was produced as a part of `npm run build` on the root `/`
	*/
	http.Handle("/", http.FileServer(http.Dir("./build")))

	/*
		Check if the port environment variable has been set and if so, use that, otherwise let's use a reasonable default
	*/
	if !(port == "") {
		log.Fatal(http.ListenAndServe(":"+port, nil))
	} else {
		log.Fatal(http.ListenAndServe(":"+defaultPort, nil))
	}
}

```

Caveat: Heroku gives us a port to bind our web application to, so we grab that from an environment variable, you can read more about it [here](https://devcenter.heroku.com/articles/runtime-principles#web-servers)

# Build a `production` version of our application

From our current directory `../deployment-demo/server` go up a level back to the root of our React project, and run:

`npm run build`

This will produce a directory called `build`.

Let's copy this into our `server` directory, which will serve as our production payload.

`cp -r build/ server/`

We can now navigate back into our `server` directory and start deploying.

# Docker

Create the following Dockerfile in the **server** directory:

```
# Stage 1
FROM golang:alpine as builder
RUN apk update && apk add --no-cache git
RUN mkdir /build 
ADD . /build/
WORKDIR /build
RUN go get -d -v
RUN go build -o deployment-demo .
# Stage 2
FROM alpine
RUN adduser -S -D -H -h /app appuser
USER appuser
COPY --from=builder /build/ /app/
WORKDIR /app
CMD ["./deployment-demo"]
```

# Heroku

## Create an app

If you don't have a Heroku account, go and [create](https://signup.heroku.com/) one! (it should take less than 5 minutes)

Once that's done, you're going to need the Heroku CLI, which can be easily installed on Ubuntu by running:

`sudo snap install heroku --classic`

After installing the CLI, run the following command to login:

`heroku login`

This will open a browser window for you to login (it's an extremely cool system)

Now run:

`heroku create [YOUR_APP_NAME]`

in the `deployment-demo/server/` directory.

An app will be created for you on your Heroku account and you should be able to see it on your [dashboard](https://dashboard.heroku.com/)

## The Container Registry

The Heroku Container Registry allows you to deploy Docker images to Heroku.

You can read more about it [here](https://devcenter.heroku.com/articles/container-registry-and-runtime).

Run the following to login in to the registry:

`heroku container:login`

Run this command to build your Docker image and push it to Heroku:

`heroku container:push web --app [YOUR_APP_NAME]`

Notice that we specify **web**, this indicates the process type we want to associate with this application. Further reading on process types can be found [here](https://devcenter.heroku.com/articles/process-model).

And finally, run this to release the image to your application:

`heroku container:release web --app [YOUR_APP_NAME]`

We should now be able to navigate to our application hosted on Heroku by running:

`heroku open --app [YOUR_APP_NAME]`

And we should see the following:

![final application running on Heroku](https://thepracticaldev.s3.amazonaws.com/i/0rfhrd634snglvyavvgj.png)

That's it! That's all you need to do to deploy a Docker image running a Go server that serves a React application to Heroku!