For a couple of years now, I've been working on a self-hosted family budgeting
application called Twigs.
Seeing as I'm an Android developer, I spend most of my days writing Java and
Kotlin. Thus, it seemed like a good idea to stick with the languages I'm most
comfortable with for my backend solution. After doing a little research, it
seemed to me like Spring Boot was quite popular, so I figured I'd also be doing
myself a favor by learning something I could possibly apply on the job someday.
Unfortunately, my simple app frequently occupies around 500MB of RAM on my
small VPS with only 2GB of RAM total, and it's not the only server I have
running there, so I've been quite discontent with this. I have been
considering rewriting the backend in Go or Rust to reduce the memory footprint
of the app, but even for such a small application, that's no small feat. In an
effort to save myself some time and energy rewriting the whole thing, I've been
looking around for ways to reduce the memory consumption of this app. Much to
my surprise I found quite a simple solution that had a massive impact. Here was
my Dockerfile
before, where the app would frequently idle at around 500MB of
RAM usage:
FROM openjdk:14-jdk as builder
MAINTAINER William Brawner <me@wbrawner.com>
RUN groupadd --system --gid 1000 gradle \
&& useradd --system --gid gradle --uid 1000 --shell /bin/bash --create-home gradle
COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src
RUN /home/gradle/src/gradlew --console=plain --no-daemon bootJar
FROM openjdk:14-jdk-slim
EXPOSE 8080
COPY --from=builder /home/gradle/src/api/build/libs/api.jar twigs-api.jar
CMD /usr/local/openjdk-14/bin/java $JVM_ARGS -jar /twigs-api.jar
Here's the updated Dockerfile
for the same app with much less memory usage:
FROM openjdk:14-jdk as builder
MAINTAINER William Brawner <me@wbrawner.com>
RUN groupadd --system --gid 1000 gradle \
&& useradd --system --gid gradle --uid 1000 --shell /bin/bash --create-home gradle
COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src
RUN /home/gradle/src/gradlew --console=plain --no-daemon bootJar
FROM adoptopenjdk:openj9
EXPOSE 8080
COPY --from=builder /home/gradle/src/api/build/libs/api.jar twigs-api.jar
CMD /opt/java/openjdk/bin/java $JVM_ARGS -jar /twigs-api.jar
The change might be easy to miss, so here's the breakdown: I have two containers involved in the build process: the first takes in the source code and compiles it into a JAR file, then the second container copies that JAR file out and simply executes it. The change I made to reduce the memory was simply going from OpenJDK to OpenJ9, which brought my memory usage down from around 500MB while idle to around 150MB idle. While that's still a bit more than I'd like for a super simple app, it's little enough that I can stop worrying about it for the time being. I've had this running for a few days now without any noticeable side-effects, so I'm hoping this will be a long-term solution for me. Should anything come up though, I'll be sure to write a follow-up post in the future.