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]
$ heroku buildpacks:remove heroku-community/static
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;
}
}
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
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
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"
...
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",
...
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