Deploying Quarkus/PostgreSQL and Angular/Nginx on Heroku as containers
This is a simple tutorial explaining one way of deploying Quarkus/PostgreSQL and Angular/Nginx on Heroku as containers.
Let’s start!
Create the Docker images locally
Clone my demo project:
$ git clone https://github.com/felipewind/heroku-quarkus-angular-docker
Run the script
Run this script to build Quarkus, Angular and then to bring up the docker-compose:
$ ./run-with-local-build.sh
Or do it manually
Build the Quarkus project:
$ cd quarkus/
$ ./mvnw package
Build the Angular project:
$ cd angular/
$ npm install
$ ng build
Bring up the following docker-compose-yml:
$ docker-compose up --build
Images ready and containers running
Now, if everything worked well, you will have three Docker images created:
$ docker images
REPOSITORY TAG
heroku/quarkus-jvm 1.0
heroku/angular 1.0
postgres 13.3
And three containers running.
It’s possible to access the Quarkus Swagger UI at http://localhost:8080/q/swagger-ui/
And the Angular front end at http://localhost
Deploy Quarkus/PostgreSQL on Heroku
After creating your account on Heroku, click on option “Create new app” and chose any name you like.
Click on the Resource tab and select “Heroku Postgres”:
Select the plan you prefer (HobbyDev is free) and now your Heroku application has one PostgreSQL database:
Configuring Quarkus
All we have to do now is push our Quarkus image to the Heroku registry and release it! The executing container will communicate with the database and will expose its endpoints.
Here is the most important part! Our application must receive the database and HTTP port as configuration parameters. To achieve this, we must define these parameters on the application.properties:
This way, our Quarkus container will receive all these parameters from the Heroku environment on deploy time.
PORT parameter
The Heroku application can run two types of processes: WEB and WORKER. The WEB one will be able to receive calls from the outer world. Heroku passes one PORT environment parameter to the WEB container. Each release of your Quarkus container can have a different PORT value and your application must receive this variable dynamically.
Database parameters
We must set the configuration variables of the database so they will match with the Quarkus ones we defined.
Click on the “Settings” tab and then on the “Reveal Config Vars” button:
Heroku DATABASE_URL variable problem
Now we have a problem, the only variable PostgreSQL exposes is DATABASE_URL in a pattern that isn’t the same that Quarkus expects.
Pattern of DATABASE_URL from Heroku:
postgres://username:password@host:port/databasename
Pattern of JDBC variables of Quarkus:
quarkus.datasource.jdbc.url=jdbc:postgresql://host:port/databasename
quarkus.datasource.username=username quarkus.datasource.password=password
Heroku DATABASE_URL variable solution
In this story I explain better about this problem and the two ways that I found to solve it:
Shortly, you have two options
Manual extraction
Create the Quarkus variables on the Heroku Config Vars (DB_JDBC_URL, DB_JDBC_USER and DB_JDBC_PASSWORD) passing the values you extracted from the DATABASE_URL variable.
Using the bash script to extract
Create one variable called DB_HEROKU_SPLIT and set it to true:
The above DB_ECHO_VALUES is optional, if true, the data base values will be printed on the logs.
The heroku.sh script, that is on the root folder of the demo project, will automatically create the Quarkus data source variables from the Heroku DATABASE_URL. I put this script on the Docker file startup command.
Release the Quarkus image on Heroku
Heroku website has a good documentation on how to do this:
Install the Heroku CLI on your machine.
Login on registry.heroku.com:
heroku container:login
Tag your image (<app> is your Heroku app name):
docker tag heroku/quarkus-jvm:1.0 registry.heroku.com/<app>/web
Push it to the registry:
docker push registry.heroku.com/<app>/web
Release it:
heroku container:release web --app <app>
Check the logs:
heroku logs --app <app> --tail
Click on the Open app button on the top right of your Heroku app managing:
And bingo! You will see the main page of your Quarkus application:
On the end of the URL, just add /q/swagger-ui/ and you will be able to test your app:
The Quarkus part is done !!!
Deploy Angular/Nginx on Heroku
The Angular process will be very similar to the Quarkus process we did before.
Create one new Heroku app to host your Angular front end.
PORT parameter
This app will need to receive the PORT parameter from Heroku and will need to communicate with the Quarkus back end.
On the Angular root folder, create the “nginx” folder and this “default.conf.template” file:
server {
listen $PORT default_server;location / {
root /usr/share/nginx/html;
index index.html;
}
}
On the start of your Nginx container, include this command:
envsubst '$PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf
API dynamic URL
You can put the URL from your back end directly on the environment.prod.ts file, but here I use a trick to set it dynamically.
The trick is to declare one template name on the environment.prod.ts file and replace this name on the docker command that starts your project.
environment.prod.ts:
export const environment = {
production: true,
apiUrl: 'HEROKU_API_URL'
};
On the Docker startup command, include this:
sed -i s#HEROKU_API_URL#$API_URL#g /usr/share/nginx/html/main.*.js
Create the API_URL variable on the Heroku Config Vars and set the URL of your back end project:
Dockerfile
Here is the complete Dockerfile containing the PORT and the dynamic API_URL trick:
Release the Angular container
Do the same steps of Quarkus (tag, push, release and check de logs) with your Heroku front and app name:
docker tag heroku/angular:1.0 registry.heroku.com/<app>/web
docker push registry.heroku.com/<app>/web
heroku container:release web --app angular-as-container
heroku logs --app angular-as-container --tail
Now click on the “Open app” button of your Angular Heroku app and you should see this simple CRUD front end:
Remember that the Heroku app will be dormant after 30 minutes of inactivity, so if your back end is asleep the HTTP GET requisition from your front end will take some time to answer.
Final words
I hope you enjoyed this tutorial!
If I told something wrong or you had some problem when following it, please just say it on the comments.
Credits
Tutorial explaining how to create one Quarkus application from scratch and how to deploy it on Heroku from a GitHub project:
About the idea of creating one dynamic URL on Angular:
StackOverflow question explaining how to configure the Nginx PORT:
Quarkus Guide to deploy on Heroku: