diff --git a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
index cee4bf331119b01b2d0ccdeddd2f6922159864f4..821cf0776dca991dd58c08ba9b8c11bf29d25bb0 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
@@ -455,7 +455,7 @@ public class RouterConsoleRunner implements RouterApp {
             System.err.println("ERROR: Unable to create Jetty temporary work directory");
 
         // so Jetty can find WebAppConfiguration
-        System.setProperty("jetty.class.path", _context.getBaseDir() + "/lib/routerconsole.jar");
+        System.setProperty("jetty.class.path", (new File(_context.getLibDir(), "routerconsole.jar")).getPath());
         // FIXME
         // http://dev.eclipse.org/mhonarc/lists/jetty-users/msg03487.html
         //_server.setGracefulShutdown(1000);
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/WebAppConfiguration.java b/apps/routerconsole/java/src/net/i2p/router/web/WebAppConfiguration.java
index ec671bb7d589064fefa01bd979bd697c1227ff15..16ad05885a4a9c3c62273cd7da7866ef5f47d8d0 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/WebAppConfiguration.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/WebAppConfiguration.java
@@ -87,7 +87,7 @@ public class WebAppConfiguration implements Configuration {
 ****/
 
         I2PAppContext i2pContext = I2PAppContext.getGlobalContext();
-        File libDir = new File(i2pContext.getBaseDir(), "lib");
+        File libDir = i2pContext.getLibDir();
         // FIXME this only works if war is the same name as the plugin
         File pluginDir = new File(i2pContext.getConfigDir(),
                                         PluginStarter.PLUGIN_DIR + ctxPath);
@@ -180,7 +180,7 @@ public class WebAppConfiguration implements Configuration {
         } else {
             // Java 9 - assume everything in lib/ is in the classpath
             // except addressbook.jar
-            File libDir = new File(ctx.getBaseDir(), "lib");
+            File libDir = ctx.getLibDir();
             File[] files = libDir.listFiles(new FileSuffixFilter(".jar"));
             if (files != null) {
                 for (int i = 0; i < files.length; i++) {
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/FileDumpHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/FileDumpHelper.java
index 5fb4b238aa2fc908d89c6d7f748215330eb6665b..769ea4e70041057a8624f8b07aff59deb6872c8e 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/FileDumpHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/FileDumpHelper.java
@@ -63,7 +63,7 @@ public class FileDumpHelper extends HelperBase {
         }
 
         // our jars
-        File dir = new File(_context.getBaseDir(), "lib");
+        File dir = _context.getLibDir();
         buf.append("<tr><th class=\"subheading routerfiles\" colspan=\"9\"><b>Router Jar Files:</b> <code>");
         buf.append(dir.getAbsolutePath());
         buf.append("</code></th></tr>\n");
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/LogsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/LogsHelper.java
index f2d4ec023acab06702fbcd0997b58016257455f4..c1aa060533ef50f270f524e4d745bfea12da9300 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/LogsHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/LogsHelper.java
@@ -211,8 +211,8 @@ public class LogsHelper extends HelperBase {
      * @since 0.9.35
      */
     public String getBuiltBy() {
-        File baseDir = _context.getBaseDir();
-        File f = new File(new File(baseDir, "lib"), "i2p.jar");
+        File libDir = _context.getLibDir();
+        File f = new File(libDir, "i2p.jar");
         Attributes att = FileDumpHelper.attributes(f);
         if (att != null) {
             String s = FileDumpHelper.getAtt(att, "Built-By");
diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java
index 7b89f1b7b8fa87d8a18734696aa6afe3ff4cf1de..4e97e05a67fa2b9a793e89e128f0cfdb48c44830 100644
--- a/core/java/src/net/i2p/I2PAppContext.java
+++ b/core/java/src/net/i2p/I2PAppContext.java
@@ -111,6 +111,7 @@ public class I2PAppContext {
     private final File _logDir;
     private final File _appDir;
     private volatile File _tmpDir;
+    private final File _libDir;
     private final Random _tmpDirRand = new Random();
     private final ClientAppManager _appManager;
     // split up big lock on this to avoid deadlocks
@@ -317,6 +318,13 @@ public class I2PAppContext {
         } else {
             _appDir = _routerDir;
         }
+
+        s = getProperty("i2p.dir.lib");
+        if (s != null) {
+            _libDir = new File(s);
+        } else {
+            _libDir = new File(_baseDir, "lib");
+        }
         String isPortableStr = System.getProperty("i2p.dir.portableMode");
         boolean isPortable = Boolean.parseBoolean(isPortableStr);
         if (isPortable) {
@@ -438,6 +446,13 @@ public class I2PAppContext {
         }
     }
 
+    /**
+     *  This is the library dir, which is usually $I2P/lib.
+     *  @return dir constant for the life of the context
+     *  @since 0.9.52
+     */
+    public File getLibDir() { return _libDir; }
+
     /** don't rely on deleteOnExit() */
     public void deleteTempDir() {
         synchronized (_lock1) {
diff --git a/core/java/src/net/i2p/crypto/KeyStoreUtil.java b/core/java/src/net/i2p/crypto/KeyStoreUtil.java
index 3cefca43e8d5b90c6ff7d3dc20e256134f319adc..b1375ca350e1d2eaddaa9692d28f1d58223ed65d 100644
--- a/core/java/src/net/i2p/crypto/KeyStoreUtil.java
+++ b/core/java/src/net/i2p/crypto/KeyStoreUtil.java
@@ -935,8 +935,7 @@ public final class KeyStoreUtil {
         a.add("-keysize");   a.add(Integer.toString(keySize));
         a.add("-keypass");   a.add(keyPW);
         if (keyAlg.equals("Ed") || keyAlg.equals("EdDSA") || keyAlg.equals("ElGamal")) {
-            File f = I2PAppContext.getGlobalContext().getBaseDir();
-            f = new File(f, "lib");
+            File f = I2PAppContext.getGlobalContext().getLibDir();
             f = new File(f, "i2p.jar");
             // providerpath is not in the man page; see keytool -genkey -help
             a.add("-providerpath");  a.add(f.getAbsolutePath());
diff --git a/core/java/src/net/i2p/time/BuildTime.java b/core/java/src/net/i2p/time/BuildTime.java
index f3d52deb5d7b5a1c343aecc192954cc681b0a6d1..96706e86e0e687f7923dfd81af9c00137910a860 100644
--- a/core/java/src/net/i2p/time/BuildTime.java
+++ b/core/java/src/net/i2p/time/BuildTime.java
@@ -116,7 +116,7 @@ public class BuildTime {
     private static long getBuildTime(SimpleDateFormat fmt, String jar) {
         if (SystemVersion.isAndroid())
             return 0;
-        File f = new File(I2PAppContext.getGlobalContext().getBaseDir(), "lib");
+        File f = I2PAppContext.getGlobalContext().getLibDir();
         f = new File(f, jar);
         Attributes atts = attributes(f);
         if (atts == null)
diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java
index 151d8d7c566b629d7ce547bf26e24532bcdf0b6c..1576e44feb90144ad86f52ad54b50f3489961767 100644
--- a/router/java/src/net/i2p/router/Router.java
+++ b/router/java/src/net/i2p/router/Router.java
@@ -1349,7 +1349,7 @@ public class Router implements RouterClock.ClockShiftListener {
                     // only do this on these OSes
                     boolean goodOS = isWin || isMac ||
                                      osName.contains("linux") || osName.contains("freebsd");
-                    File jbigiJar = new File(_context.getBaseDir(), "lib/jbigi.jar");
+                    File jbigiJar = new File(_context.getLibDir(), "jbigi.jar");
                     if (goodOS && jbigiJar.exists() && _context.getBaseDir().canWrite()) {
                         String libPrefix = isWin ? "" : "lib";
                         String libSuffix = isWin ? ".dll" : isMac ? ".jnilib" : ".so";
diff --git a/router/java/src/net/i2p/router/tasks/InstallUpdate.java b/router/java/src/net/i2p/router/tasks/InstallUpdate.java
index aa706c126f69abfa60a7783fc674949e93b2b1eb..0e43a555cfa9980ee07cdfd3cf7426d3dacde307 100644
--- a/router/java/src/net/i2p/router/tasks/InstallUpdate.java
+++ b/router/java/src/net/i2p/router/tasks/InstallUpdate.java
@@ -135,7 +135,7 @@ public class InstallUpdate {
         boolean goodOS = isWin || isMac ||
                          osName.contains("linux") || osName.contains("freebsd");
 
-        File jbigiJar = new File(context.getBaseDir(), "lib/jbigi.jar");
+        File jbigiJar = new File(context.getLibDir(), "jbigi.jar");
         if (goodOS && jbigiJar.exists()) {
             String libPrefix = (isWin ? "" : "lib");
             String libSuffix = (isWin ? ".dll" : isMac ? ".jnilib" : ".so");