Skip to content

Instantly share code, notes, and snippets.

@steveclarke
Last active April 5, 2023 19:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steveclarke/b613ddcd51063351f7f43bb794777767 to your computer and use it in GitHub Desktop.
Save steveclarke/b613ddcd51063351f7f43bb794777767 to your computer and use it in GitHub Desktop.

Steps to Configure Heroku Static

Add Buildpacks

We need to add two buildpacks.

heroku-community/nginx replaces the deprecated heroku-community/static and allows us to configure NGINX to redirect all traffic to dist/spa/index.html (see NGINX Configuration).

heroku/nodejs allows to comple the application on the server.

$ heroku buildpacks:add heroku-community/nginx -r [heroku remote name]
$ heroku buildpacks:add heroku/nodejs -r [heroku remote name]

Remove Heroku Static Buildpack

$ heroku buildpacks:remove heroku-community/static

NGINX Configuration

Create config\nginx.conf.erb with the following contents. The heroku-community/nginx buildpack expects this file.

daemon off;
# Heroku dynos have at least 4 cores.
worker_processes <%= ENV['NGINX_WORKERS'] || 4 %>;

events {
	use epoll;
	accept_mutex on;
	worker_connections <%= ENV['NGINX_WORKER_CONNECTIONS'] || 1024 %>;
}

http {
	gzip on;
	gzip_comp_level 2;
	gzip_min_length 512;
	gzip_proxied any; # Heroku router sends Via header

	server_tokens off;

	log_format l2met 'measure#nginx.service=$request_time request_id=$http_x_request_id';
	access_log <%= ENV['NGINX_ACCESS_LOG_PATH'] || 'logs/nginx/access.log' %> l2met;
	error_log <%= ENV['NGINX_ERROR_LOG_PATH'] || 'logs/nginx/error.log' %>;


	include mime.types;
	default_type application/octet-stream;
	sendfile on;

	# Must read the body in 5 seconds.
	client_body_timeout <%= ENV['NGINX_CLIENT_BODY_TIMEOUT'] || 5 %>;


	server {
		location / {
			add_header 'Cache-Control' "public, max-age=3600";
			try_files $uri $uri/ /index.html;
		}
		
	        # Redirect all non-https traffic to https
    	        if ($http_x_forwarded_proto != "https") {
	          return 301 https://$host$request_uri;
    		}

		listen <%= ENV["PORT"] %>;
		server_name _;
		keepalive_timeout 5;
		client_max_body_size <%= ENV['NGINX_CLIENT_MAX_BODY_SIZE'] || 1 %>M;

		root dist/spa;
	}
}

Create Procfile

heroku-community/nginx expects a Procfile to instruct it on what service to start. We want to use the solo instance of NGINX so our Procfile should contain:

web: bin/start-nginx-solo

Environment Variables for Compilation on Server

The app needs to be compiled on the server because it's (surprisingly) even more complex to compile it locally. Because deploys on Heroku have to be done using git, we would need to commit the built code and artifacts in dist/spa into our git repo. However this would cause complexity in dealing with staging/production builds. What's in dist/spa currently? Production or staging code?

The app is built to use .quasar.env.json to access variables that differ between app environments but can't really be checked into the repo because development environments might need different settings for the "development" node.

The solution we came up with was to add a JSON node with variables to the Heroku environment variables and render that to a file just before building the app. The bin/makeenv script simply echoes out the contents of $QUASAR_ENV to .quasar.env.json on the server. The node is named "heroku". Then our heroku-postbuild script in package.json sets the QENV to heroku and builds.

Add QUASAR_ENV JSON snippet to Heroku environment variables.

{
  "heroku": {
    "APP_ENV": "production",
    "API_URL": "https://example.com/api/v1",
    "GOOGLE_MAPS_API_KEY": "",
    "ANALYTICS_ID": "",
    "CDN_URL": "",
    "GUEST_USER": "",
    "GUEST_PASS": ""
  }
}

Add bin/makeenv:

#!/usr/bin/env bash

echo $QUASAR_ENV > .quasar.env.json

Build Script

Add a heroku-postbuild script to package.json. This is what builds the application on the server.

"scripts": {
  "heroku-postbuild": "bin/makeenv && QENV=heroku quasar build"
...

Set target Node version

This is what tells the server what version of Node to use. The default settings may end up installing a more current version of Node, but the app must run no later than 16.x

"engines": {
  "node": "16.x",
  ...

Reference Links

heroku/heroku-buildpack-nginx - Buildpacks - Heroku Elements

Discussion on migrating heroku/heroku-buildpack-static#246

Sample app with Vue https://github.com/senolatac/vue-device-seller/tree/nginx-support

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment