diff --git a/Dockerfile b/Dockerfile
index c91cb67cd77299c22cac329cc25583697b50ceb1..bbdca32411825b8a009b6f10732527916e80d254 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -33,6 +33,7 @@ COPY gradlew .
 COPY nshmp-lib ../nshmp-lib
 COPY settings.gradle .
 COPY src src
+COPY openapi.properties .
 
 RUN ./gradlew --no-daemon assemble \
     && mv ${libs_dir}/*-all.jar ${jar_file}
diff --git a/build.gradle b/build.gradle
index 21bccd4e49d40b33e478fddeab2ec2555fc35ed2..f57cf204c1fdcfb6cf0314dfc6c900d7ca875499 100644
--- a/build.gradle
+++ b/build.gradle
@@ -49,8 +49,9 @@ dependencies {
   runtimeOnly "ch.qos.logback:logback-classic:${logbackVersion}"
 
   // Swagger
-  annotationProcessor("io.micronaut.configuration:micronaut-openapi:${swaggerVersion}")
+  annotationProcessor("io.micronaut.configuration:micronaut-openapi:${mnOpenAPIVersion}")
   implementation("io.swagger.core.v3:swagger-annotations:${swaggerVersion}")
+  implementation("io.swagger.core.v3:swagger-models:${swaggerVersion}")
 
   // junit
   testAnnotationProcessor "io.micronaut:micronaut-inject-java:${mnVersion}"
@@ -62,6 +63,10 @@ dependencies {
   testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine"
 }
 
+ext {
+  swaggerDir = "swagger-files"
+}
+
 test {
   useJUnitPlatform()
 }
@@ -99,3 +104,18 @@ tasks.withType(com.github.spotbugs.SpotBugsTask) {
     html.enabled = true
   }
 }
+
+task swagger(type: JavaExec) {
+  doFirst {
+    mkdir swaggerDir
+  }
+  classpath sourceSets.main.runtimeClasspath
+  main = "gov.usgs.earthquake.nshmp.netcdf.swagger.UpdateSwagger"
+}
+
+task swaggerClean(type: Delete) {
+  delete swaggerDir
+}
+
+clean.dependsOn swaggerClean
+assemble.dependsOn swagger
diff --git a/gradle.properties b/gradle.properties
index df92924b30e774ba1df0c38de7e474e9a66a5308..17f0e3514c60deaffdf5c3d95699838c9d2e5c59 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -5,4 +5,5 @@ logbackVersion = 1.2.3
 mnVersion = 1.3.2
 netcdfVersion = 5.1.0
 slfVersion = 1.7.30
-swaggerVersion = 1.4.0
+swaggerVersion = 2.1.1
+mnOpenAPIVersion = 1.4.0
diff --git a/openapi.properties b/openapi.properties
index 626d1e43053fde40c7f14ad0e275a477e385b86a..3f88279c7ecf8e0b404ca95acae374469195acec 100644
--- a/openapi.properties
+++ b/openapi.properties
@@ -1 +1 @@
-micronaut.openapi.target.file = build/resources/main/swagger/netcdf-swagger.yml
+micronaut.openapi.target.file = build/classes/java/main/META-INF/swagger/nshmp-netcdf-openapi.yml
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NshmGroup.java b/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NshmGroup.java
index 0f6d079d6dc08fff83336b232b2ddc3ca3e13d31..e3adbaab9289e20290f5126e37341ddac750bf84 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NshmGroup.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NshmGroup.java
@@ -1,5 +1,7 @@
 package gov.usgs.earthquake.nshmp.netcdf;
 
+import com.google.common.base.CaseFormat;
+
 public enum NshmGroup {
 
   CONUS_2018(
@@ -22,6 +24,10 @@ public enum NshmGroup {
     return display;
   }
 
+  public String model() {
+    return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name());
+  }
+
   /**
    * Returns the NetCDF target base group to read.
    */
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/netcdf/swagger/UpdateSwagger.java b/src/main/java/gov/usgs/earthquake/nshmp/netcdf/swagger/UpdateSwagger.java
new file mode 100644
index 0000000000000000000000000000000000000000..9f70441a2e2b89bab4c85ffe6a1dad919b0caec6
--- /dev/null
+++ b/src/main/java/gov/usgs/earthquake/nshmp/netcdf/swagger/UpdateSwagger.java
@@ -0,0 +1,152 @@
+package gov.usgs.earthquake.nshmp.netcdf.swagger;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.Properties;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+import gov.usgs.earthquake.nshmp.netcdf.NshmGroup;
+
+import io.swagger.v3.oas.models.PathItem;
+
+/**
+ * Create Swagger files for each model and update context path.
+ * 
+ * @author U.S. Geological Survey
+ */
+class UpdateSwagger {
+
+  private static final Path SWAGGER_DIR = Paths.get("swagger-files");
+  private static final Path TARGET_FILE;
+  private static final String OPENAPI_TARGET = "micronaut.openapi.target.file";
+
+  static {
+    try (var file = new FileInputStream("openapi.properties")) {
+      var properties = new Properties();
+      properties.load(file);
+      var openapi = properties.getProperty(OPENAPI_TARGET);
+      TARGET_FILE = Paths.get(openapi);
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public static void main(String[] args) {
+    run();
+  }
+
+  private static void run() {
+    for (var nshmGroup : NshmGroup.values()) {
+      updateSwaggerFile(TARGET_FILE, nshmGroup.model());
+    }
+  }
+
+  private static void updateSwaggerFile(Path yamlPath, String contextPath) {
+    var options = new DumperOptions();
+    options.setPrettyFlow(true);
+    options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+
+    var yaml = new Yaml(options);
+    var out = SWAGGER_DIR.resolve("netcdf-swagger-" + contextPath + ".yml");
+
+    try {
+      var yamlFile = yaml.loadAs(new FileReader(yamlPath.toFile()), Swagger.class);
+      var paths = new io.swagger.v3.oas.models.Paths();
+
+      for (var entry : yamlFile.getPaths().entrySet()) {
+        paths.addPathItem("/" + contextPath + entry.getKey(), entry.getValue());
+      }
+
+      yamlFile.paths = paths;
+      yaml.dump(yamlFile, new FileWriter(out.toFile()));
+    } catch (FileNotFoundException e) {
+      throw new RuntimeException(e);
+    } catch (IOException e) {
+      throw new RuntimeException("Could not write to [" + out + "]");
+    }
+  }
+
+  public static class Swagger {
+    String openapi = "3.0.1";
+    Object info;
+    Map<String, PathItem> paths;
+    Object externalDocs;
+    Object servers;
+    Object tags;
+    Object components;
+    Map<String, Object> extensions;
+
+    public String getOpenapi() {
+      return openapi;
+    }
+
+    public Object getInfo() {
+      return info;
+    }
+
+    public Map<String, PathItem> getPaths() {
+      return paths;
+    }
+
+    public Object getExternalDocs() {
+      return externalDocs;
+    }
+
+    public Object getServers() {
+      return servers;
+    }
+
+    public Object getTags() {
+      return tags;
+    }
+
+    public Object getComponents() {
+      return components;
+    }
+
+    public Map<String, Object> getExtension() {
+      return extensions;
+    }
+
+    public void setOpenapi(String openapi) {
+      this.openapi = openapi;
+    }
+
+    public void setInfo(Object info) {
+      this.info = info;
+    }
+
+    public void setPaths(Map<String, PathItem> paths) {
+      this.paths = paths;
+    }
+
+    public void setExternalDocs(Object externalDocs) {
+      this.externalDocs = externalDocs;
+    }
+
+    public void setServers(Object servers) {
+      this.servers = servers;
+    }
+
+    public void setTags(Object tags) {
+      this.tags = tags;
+    }
+
+    public void setComponents(Object components) {
+      this.components = components;
+    }
+
+    public void setExtension(Map<String, Object> extensions) {
+      this.extensions = extensions;
+    }
+  }
+
+}
diff --git a/src/main/resources/swagger/index.js b/src/main/resources/swagger/index.js
index 4d3c03f3b2a22b78111eba39ee2e986c678d48fa..88964dd75e471fb14cc8ca4024496461e0fd369c 100644
--- a/src/main/resources/swagger/index.js
+++ b/src/main/resources/swagger/index.js
@@ -30,7 +30,7 @@ window.onload = function() {
       };
     },
     ui = SwaggerUIBundle({
-      url: contextPath + "/swagger/netcdf-services.yml",
+      url: contextPath + "/swagger/nshmp-netcdf-openapi.yml",
       dom_id: "#swagger-ui",
       tagsSorter: 'alpha',
       presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],