This article is part of the Java Week series:
- The Journey of Nx Gradle
- Polyglot Projects Made Easy
- Getting Mobile Into Your Monorepo
- Seamless Deploys With Docker
We've explored how you can add a Java app to an existing Nx workspace, but just getting the code in the same place doesn't really help ship our code. With JavaScript apps, you simply need to have a static hosting provider or have a platform that can run node. But Java? Where do you even begin? Let's look at how we can get our Java backend deployed and automate it using Nx. We'll also get a sneak peek at a new plugin we've been working on.
The Challenge: From Monorepo to Production
Running both the front-end and back-end within an Nx workspace is straightforward. But when it comes time to deploy your backend to a live URL, questions arise:
- How do we package and deploy a Java application from the monorepo?
- Do we need a hosting provider that supports Java by default?
- How do we ensure consistent environments across different platforms?
The solution: Docker.
Docker allows us to encapsulate our backend into an image, ensuring that it will run consistently across AWS, GCP, or any other provider. By combining Docker with nx release
, we can:
- Automate the creation of Docker images for the backend.
- Ensure builds are consistent and reproducible.
- Push images directly to registries like Docker Hub for deployment.
The upcoming @nx/docker
plugin takes this a step further, letting you define Docker workflows natively within Nx.
Setting Up a Dockerfile for the Backend
To get started, we create a Dockerfile
inside the Java backend directory of the Nx workspace. This file tells Docker how to build and run our application:
1FROM openjdk:26-slim-bullseye
2MAINTAINER baeldung.com
3EXPOSE 3000
4COPY build/libs/java-backend-0.0.1-SNAPSHOT.jar app.jar
5ENTRYPOINT ["java","-jar","/app.jar"]
6
- Base Image: Use
openjdk:26
as the starting point. - Expose Ports: Our backend runs on port
3000
, so we expose this port for the container. - Copy the Build Artifact: Copy the generated
.jar
file into the container asapp.jar
. - Set the Entry Point: Run the app with
java -jar app.jar
.
This simple setup ensures that the container will spin up our backend just like we would run it locally. Now, we can manually run Docker ourselves from the command line, but we have Nx here, and it can do that for us. Let's add the new Nx Docker plugin:
1nx add @nx/docker
2
In my project, I need to setup some port forwarding, so that the port my Java app runs on, can be exposed when the Docker image is started.
1{
2 "name": "java-backend",
3 "root": "apps/java-backend",
4 "projectType": "application",
5 "targets": {
6 "docker:run": {
7 "options": {
8 "args": ["-p", "3000:3000"]
9 }
10 }
11 }
12}
13
With this, localhost:3000
will serve your API endpoints as if they were running directly on your machine.
Now with this new plugin, you can incorporate various Docker tasks in other parts of your Nx workflow, or as stand alone tasks. We can harness Nx’s task orchestration to build out a task pipeline. We can set docker:build
to dependsOn: ["build"]
and the @nx/docker
plugin will already infer that docker:run
dependsOn: ["docker:build"]
ensuring that the docker image is available locally to run.
1nx docker:run java-backend
2
All this from a simple command!
Integrating Docker with Nx Release
Now creating and running a docker image is fine, but we want to coordinate this as part of a release so everything we built can be shipped together. This is where nx release
comes in. Let's add a new release
configuration in our nx.json
:
1{
2 "release": {
3 "projects": ["java-backend"],
4 "projectsRelationship": "independent",
5 "releaseTagPattern": "release/{projectName}/{version}",
6 "docker": {
7 "skipVersionActions": true
8 },
9 "changelog": {
10 "projectChangelogs": true
11 }
12 }
13}
14
This is fairly standard release config, but the docker
entry is new. The skipVersionActions
tells Nx to not attempt to version any of the packages we’re releasing. Typically, this means bumping the version to the next major/minor/patch depending on your commits. Since this is just a demo, we don't really need worry about versioning any packages, just versioning the Docker image.
Then, we need to include some release configuration to tell nx release
about where we want our java-backend
released to. Let's open the project.json
and add a new release setup:
1{
2 "name": "java-backend",
3 "root": "apps/java-backend",
4 "projectType": "application",
5 "targets": {...},
6 "release": {
7 "docker": {
8 "repositoryName": "nrwlmike/java-app"
9 }
10 }
11}
12
The repositoryName
is how nx release
will know where it should upload the image to after it's ready. By default, this will publish to Docker Hub, but you can also set a registryURL
to point it to your own private Docker registry if needed.
Now, it's time to ship it!
1nx release --first-release
2
Nx will build, version, and publish your Docker image to Docker Hub.
Deploying Anywhere
With the Docker image published, deployment becomes as simple as pointing any provider—AWS, GCP, or others—to your image. Your backend will spin up in a consistent, production-ready environment.
The Docker plugin is still experimental, but its integration into Nx’s existing build and release workflows makes deploying backends seamless. It takes care of the tedious steps, allowing you to focus on building features rather than managing deployment scripts.
If you're interested in knowing more, let us know! Join our community Discord and be on the look out for the official release of the Docker plugin.
Learn more: