diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0c339a0636b8d76b8ceb74ca1c79ccbaf8b6a7ba..9feeb65da5a1d6ca08cf775d432b27c5f5251494 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,5 @@
 variables:
+  GRADLE_USER_HOME: ${CI_PROJECT_DIR}/tmp/.gradle
   IMAGE_NAME: ${CODE_REGISTRY_IMAGE}/${CI_PROJECT_NAME}
   JACOCO_HTML_DIR: build/reports/jacoco/test/html
   JUNIT_FILES: build/test-results/test/TEST-*.xml
@@ -9,10 +10,16 @@ include:
     file: 'templates/library.yml'
 
 stages:
-  - image
+  - init
   - build
   - publish
 
+# Do not run for merge requests
+workflow:
+  rules:
+    - if: $CI_COMMIT_TAG
+    - if: $CI_COMMIT_BRANCH
+
 ####
 # Templates
 ####
@@ -40,36 +47,56 @@ stages:
     IMAGE_NAME: ${CI_PROJECT_NAME}
 
 ####
-# Stage: image
+# Stage: init
 ####
 
-Image:
+Init:
+  artifacts:
+    paths:
+      - '${GRADLE_USER_HOME}'
   extends:
-    - .dind
+    - .gradle
+  script:
+    - ./gradlew dependencies
+  stage: init
+
+####
+# Stage: build
+####
+
+Build Image Haz:
+  extends:
+    - .docker-build
+  variables:
+    IMAGE_NAME: nshmp-haz
+
+Build Image WS:
+  extends:
+    - .docker-build
+  variables:
+    IMAGE_NAME: nshmp-haz-ws
+    DOCKERFILE: ws.Dockerfile
+
+Build Lambda:
+  artifacts:
+    expire_in: 1 yr
+    paths:
+      - build/libs/nshmp-haz-v2.jar
+      - build/libs/nshmp-haz-dependencies.zip
+  extends:
+    - .gradle
   rules:
     -
       changes:
-        - .gitlab/Dockerfile
+        - 'src/**'
+        - '*gradle*'
       when: on_success
     -
       allow_failure: true
       when: manual
   script:
-    - |
-      docker build \
-        --build-arg FROM_IMAGE=${DEVOPS_REGISTRY}usgs/node:12 \
-        --file ".gitlab/Dockerfile" \
-        --pull \
-        --tag ${IMAGE_NAME} \
-        .
-    - docker push ${IMAGE_NAME}
-  stage: image
-  tags:
-    - build
-
-####
-# Stage: build
-####
+    - ./gradlew assemble
+    - ./gradlew libs
 
 Build Project:
   extends:
@@ -77,7 +104,8 @@ Build Project:
   rules:
     -
       changes:
-        - '**/*.java'
+        - 'src/**'
+        - '*gradle*'
       when: on_success
     -
       allow_failure: true
@@ -85,25 +113,64 @@ Build Project:
   script:
     - ./gradlew assemble
 
-Build Lambda:
-  artifacts:
-    expire_in: 1 yr
-    paths:
-      - build/libs/nshmp-haz-v2.jar
-      - build/libs/nshmp-haz-dependencies.zip
+Markdown Lint:
   extends:
     - .gradle
   rules:
     -
       changes:
-        - '**/*.java'
+        - '**/*.md'
       when: on_success
     -
       allow_failure: true
       when: manual
   script:
-    - ./gradlew assemble
-    - ./gradlew libs
+    - ./gradlew markdownlint;
+
+Spotbugs Main:
+  extends:
+    - .gradle
+  rules:
+    -
+      changes:
+        - 'src/**'
+        - '*gradle*'
+      when: on_success
+    -
+      allow_failure: true
+      when: manual
+  script:
+    - ./gradlew spotbugsMain;
+
+Spotbugs Test:
+  extends:
+    - .gradle
+  rules:
+    -
+      changes:
+        - 'src/**'
+        - '*gradle*'
+      when: on_success
+    -
+      allow_failure: true
+      when: manual
+  script:
+    - ./gradlew spotbugsTest;
+
+Spotless:
+  extends:
+    - .gradle
+  rules:
+    -
+      changes:
+        - 'src/**'
+        - '*gradle*'
+      when: on_success
+    -
+      allow_failure: true
+      when: manual
+  script:
+    - ./gradlew spotlessCheck;
 
 Unit Tests:
   artifacts:
@@ -117,7 +184,8 @@ Unit Tests:
   rules:
     -
       changes:
-        - '**/*.java'
+        - 'src/**'
+        - '*gradle*'
       when: on_success
     -
       allow_failure: true
@@ -126,19 +194,6 @@ Unit Tests:
     - ./gradlew check
     - cat ${JACOCO_HTML_DIR}/index.html
 
-Build Haz Image:
-  extends:
-    - .docker-build
-  variables:
-    IMAGE_NAME: nshmp-haz
-
-Build WS Image:
-  extends:
-    - .docker-build
-  variables:
-    IMAGE_NAME: nshmp-haz-ws
-    DOCKERFILE: ws.Dockerfile
-
 ####
 # Stage: Publish
 ####
@@ -147,7 +202,6 @@ Trigger AWS nshmp-haz:
   rules:
     -
       if: '$CI_PROJECT_NAMESPACE == "ghsc/nshmp" && $CI_COMMIT_TAG'
-    -
       changes:
         - 'src/main/java/gov/usgs/earthquake/nshmp/aws/**/*.java'
         - 'gradle/dependencies.gradle'
diff --git a/.gitlab/Dockerfile b/.gitlab/Dockerfile
deleted file mode 100644
index 179fc72342fbb5ec0b70c5227b8cb9c30b0498fa..0000000000000000000000000000000000000000
--- a/.gitlab/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-ARG FROM_IMAGE=usgs/node:12
-
-FROM ${FROM_IMAGE}
-
-ENV LANG="en_US.UTF-8"
-
-USER root
-
-RUN yum install -y git glibc-langpack-en java-11-openjdk-devel which \
-    && yum clean all
-
-USER usgs-user
diff --git a/.markdownlintignore b/.markdownlintignore
index e8239e8c00a62aba98bc05b4b469da20e3a8663c..46331cb0fe6ec705433324ea6c7a824c5766c7b0 100644
--- a/.markdownlintignore
+++ b/.markdownlintignore
@@ -4,3 +4,4 @@ build
 tmp
 libs
 node_modules
+.gradle
diff --git a/build.gradle b/build.gradle
index 28452ba5889f40e830e88ababb953ddf6752a477..50cc3dac6f9d8ccfc80739afde370efa32d707cb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -111,7 +111,7 @@ gradle.afterProject {
     into nshmpLib
   }
   apply from: "${nshmpLibGradleDir}/git-hooks.gradle"
-  apply from: "${nshmpLibGradleDir}/markdownlint.gradle"
+  apply from: "${nshmpLibGradleDir}/node.gradle"
   apply from: "${nshmpLibGradleDir}/spotbugs.gradle"
   apply from: "${nshmpLibGradleDir}/spotless.gradle"
 }
diff --git a/gradle.properties b/gradle.properties
index 36b5d6d51b45be2da670ce2c12b9834ca87228f2..cf3d628bd4f8d80c166ff747c81d619b4828de1d 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -10,7 +10,7 @@ logbackVersion = 1.2.3
 mnOpenAPIVersion = 1.4.0
 mnVersion = 1.3.2
 nodeVersion = 3.0.1
-nshmpLibVersion = 0.2.11
+nshmpLibVersion = 0.2.12
 shadowVersion = 5.2.0
 spotbugsVersion = 4.2.4
 spotlessVersion = 4.1.0