diff --git a/debian-alt/README.txt b/debian-alt/README.txt
index 8b9bbba3bf88015dffd7faddf484bdf6a9786c1f..2539682f296f8d29a7313d2839c739fbe5f0696d 100644
--- a/debian-alt/README.txt
+++ b/debian-alt/README.txt
@@ -9,6 +9,7 @@ trusty may be copied to vivid.
 trusty may be used for jessie without libjetty9-java from backports.
 xenial may be copied to yakkety, zesty
 bionic may be copied to cosmic
+disco may be copied to eoan
 
 Not maintained:
 wheezy files are not maintained. Use the precise files instead.
diff --git a/debian-alt/bionic/changelog b/debian-alt/bionic/changelog
index cf1767b22478a005adee545f9ede78fb347e34e0..261f1b9dc9671d0017fdc9679bbb1e2e4bde0901 100644
--- a/debian-alt/bionic/changelog
+++ b/debian-alt/bionic/changelog
@@ -1,3 +1,9 @@
+i2p (0.9.39-2ubuntu1) bionic; urgency=medium
+
+  * Fix compatibility with Jetty 9.4
+
+ -- zzz on i2p (key signing) <zzz@i2pmail.org>  Fri, 19 Apr 2019 12:12:12 +0000
+
 i2p (0.9.39-1ubuntu1) bionic; urgency=medium
 
   * New upstream version 0.9.39
diff --git a/debian-alt/bionic/control b/debian-alt/bionic/control
new file mode 100644
index 0000000000000000000000000000000000000000..cf49bc2c7c015d0fb31aa81e8b524b6ee1991fef
--- /dev/null
+++ b/debian-alt/bionic/control
@@ -0,0 +1,99 @@
+Source: i2p
+Maintainer: zzz <zzz@i2pmail.org>
+Section: net
+Standards-Version: 3.9.8
+Priority: optional
+Bugs: mailto:zzz@i2pmail.org
+Homepage: https://geti2p.net/
+Build-Depends: debhelper (>= 9.20160709)
+ ,ant (>= 1.8)
+ ,debconf
+ ,openjdk-8-jdk
+ ,libjetty9-java (>= 9.4)
+ ,libtaglibs-standard-jstlel-java
+ ,libtomcat8-java
+ ,dh-apparmor
+ ,bash-completion
+ ,gettext
+ ,libgetopt-java
+ ,libjson-simple-java (<< 3)
+ ,libgmp-dev (>= 2:5.0.5)
+ ,libservice-wrapper-java
+ ,po-debconf
+
+Package: i2p
+Architecture: all
+Section: net
+Priority: optional
+Depends: ${misc:Depends}, ${java:Depends}, ${shlibs:Depends},
+ adduser,
+ debconf,
+ i2p-router (>= 0.8.6-5),
+ libjbigi-jni,
+ lsb-base,
+ service-wrapper
+Description: Invisible Internet Project (I2P) - anonymous network
+ I2P is an anonymizing network, offering a simple layer that identity-sensitive
+ applications can use to securely communicate. All data is wrapped with several
+ layers of encryption, and the network is both distributed and dynamic, with no
+ trusted parties.
+ .
+ This package depends on the router, jbigi, the java service wrapper, and
+ includes support to run I2P as a daemon.
+
+Package: libjbigi-jni
+Architecture: any
+Section: java
+Priority: optional
+Depends: ${misc:Depends}, ${shlibs:Depends}, i2p-router
+Homepage: https://geti2p.net/
+Description: Invisible Internet Project (I2P) - libjbigi library
+ This Package contains the libjbigi JNI library (and on x86 platforms, jcpuid).
+ .
+ libjbigi is a math library that is part of the I2P installation.  Use of this
+ library greatly enhances the efficiency of cryptographic algorithms, such as
+ the ones used by I2P. You can expect to see a 5-7x speed improvement on certain
+ tasks, such as elGamal encryption/decryption.
+
+Package: i2p-doc
+Architecture: all
+Section: doc
+Priority: extra
+Depends: ${misc:Depends}
+Suggests: i2p, default-jdk-doc
+Description: Invisible Internet Project (I2P) - developer documentation
+ I2P is an anonymizing network, offering a simple layer that identity-sensitive
+ applications can use to securely communicate. All data is wrapped with several
+ layers of encryption, and the network is both distributed and dynamic, with no
+ trusted parties.
+ .
+ This package contains the Javadoc files.
+
+Package: i2p-router
+Architecture: all
+Section: net
+Priority: optional
+Depends: ${misc:Depends}, ${java:Depends}, ${shlibs:Depends},
+ openjdk-9-jre-headless | openjdk-8-jre-headless | default-jre-headless | java9-runtime-headless | java8-runtime-headless,
+ geoip-database,
+ gettext-base,
+ libgetopt-java,
+ libjson-simple-java (<< 3),
+ libjetty9-java (>= 9.4),
+ libtaglibs-standard-jstlel-java,
+ libtomcat8-java,
+ famfamfam-flag-png
+Replaces: i2p ( << 0.8.6-5)
+Breaks: i2p (<< 0.8.6-5)
+Recommends: libjbigi-jni, fonts-dejavu
+Suggests: apparmor
+ ,privoxy
+ ,syndie
+Description: Invisible Internet Project (I2P) - Router
+ I2P is an anonymizing network, offering a simple layer that identity-sensitive
+ applications can use to securely communicate. All data is wrapped with several
+ layers of encryption, and the network is both distributed and dynamic, with no
+ trusted parties.
+ .
+ TrueType fonts (such as those provided in the package fonts-dejavu) are required
+ in order to generate graphs.
diff --git a/debian-alt/bionic/patches/0002-jetty-compatibility.patch b/debian-alt/bionic/patches/0002-jetty-compatibility.patch
new file mode 100644
index 0000000000000000000000000000000000000000..95cd92d91e7a533830df5fa83897b3446b9ea18c
--- /dev/null
+++ b/debian-alt/bionic/patches/0002-jetty-compatibility.patch
@@ -0,0 +1,126 @@
+--- a/apps/jetty/java/src/net/i2p/jetty/JettyXmlConfigurationParser.java
++++ b/apps/jetty/java/src/net/i2p/jetty/JettyXmlConfigurationParser.java
+@@ -43,9 +43,9 @@
+     private static XmlParser initParser()
+     {
+         XmlParser parser = new XmlParser();
+-        URL config60 = Loader.getResource(XmlConfiguration.class, "org/eclipse/jetty/xml/configure_6_0.dtd");
+-        URL config76 = Loader.getResource(XmlConfiguration.class,"org/eclipse/jetty/xml/configure_7_6.dtd");
+-        URL config90 = Loader.getResource(XmlConfiguration.class,"org/eclipse/jetty/xml/configure_9_0.dtd");
++        URL config60 = Loader.getResource("org/eclipse/jetty/xml/configure_6_0.dtd");
++        URL config76 = Loader.getResource("org/eclipse/jetty/xml/configure_7_6.dtd");
++        URL config90 = Loader.getResource("org/eclipse/jetty/xml/configure_9_0.dtd");
+         parser.redirectEntity("configure.dtd",config90);
+         parser.redirectEntity("configure_1_0.dtd",config60);
+         parser.redirectEntity("configure_1_1.dtd",config60);
+--- a/apps/jetty/java/src/net/i2p/servlet/I2PDefaultServlet.java
++++ b/apps/jetty/java/src/net/i2p/servlet/I2PDefaultServlet.java
+@@ -132,7 +132,6 @@
+      *
+      * Get the resource list as a HTML directory listing.
+      */
+-    @Override
+     protected void sendDirectory(HttpServletRequest request,
+             HttpServletResponse response,
+             Resource resource,
+--- a/apps/jetty/java/src/net/i2p/jetty/I2PRequestLog.java
++++ b/apps/jetty/java/src/net/i2p/jetty/I2PRequestLog.java
+@@ -317,7 +317,7 @@
+                 buf.append(request.getMethod());
+                 buf.append(' ');
+                 
+-                request.getUri().writeTo(u8buf);
++                u8buf.append(request.getHttpURI().toString());
+                 
+                 buf.append(' ');
+                 buf.append(request.getProtocol());
+--- a/apps/routerconsole/java/src/net/i2p/router/web/HostCheckHandler.java
++++ b/apps/routerconsole/java/src/net/i2p/router/web/HostCheckHandler.java
+@@ -15,7 +15,7 @@
+ import net.i2p.util.PortMapper;
+ 
+ import org.eclipse.jetty.server.Request;
+-import org.eclipse.jetty.servlets.gzip.GzipHandler;
++import org.eclipse.jetty.server.handler.gzip.GzipHandler;
+ 
+ /**
+  * Block certain Host headers to prevent DNS rebinding attacks.
+--- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
++++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
+@@ -22,6 +22,7 @@
+ import java.util.SortedSet;
+ import java.util.StringTokenizer;
+ import java.util.concurrent.LinkedBlockingQueue;
++import javax.servlet.ServletRequest;
+ 
+ import net.i2p.I2PAppContext;
+ import net.i2p.app.ClientApp;
+@@ -48,6 +49,7 @@
+ import org.eclipse.jetty.security.HashLoginService;
+ import org.eclipse.jetty.security.ConstraintMapping;
+ import org.eclipse.jetty.security.ConstraintSecurityHandler;
++import org.eclipse.jetty.security.UserStore;
+ import org.eclipse.jetty.security.authentication.DigestAuthenticator;
+ import org.eclipse.jetty.server.AbstractConnector;
+ import org.eclipse.jetty.server.ConnectionFactory;
+@@ -959,6 +961,8 @@
+             } else {
+                 HashLoginService realm = new CustomHashLoginService(JETTY_REALM, context.getContextPath(),
+                                                                     ctx.logManager().getLog(RouterConsoleRunner.class));
++                UserStore userStore = new UserStore();
++                realm.setUserStore(userStore);
+                 sec.setLoginService(realm);
+                 sec.setAuthenticator(authenticator);
+                 String[] role = new String[] {JETTY_ROLE};
+@@ -966,7 +970,7 @@
+                     String user = e.getKey();
+                     String pw = e.getValue();
+                     Credential cred = Credential.getCredential(MD5_CREDENTIAL_TYPE + pw);
+-                    realm.putUser(user, cred, role);
++                    userStore.addUser(user, cred, role);
+                     Constraint constraint = new Constraint(user, JETTY_ROLE);
+                     constraint.setAuthenticate(true);
+                     ConstraintMapping cm = new ConstraintMapping();
+@@ -986,7 +990,7 @@
+                         try {
+                             // each char truncated to 8 bytes
+                             String user2 = new String(b2, "ISO-8859-1");
+-                            realm.putUser(user2, cred, role);
++                            userStore.addUser(user2, cred, role);
+                             constraint = new Constraint(user2, JETTY_ROLE);
+                             constraint.setAuthenticate(true);
+                             cm = new ConstraintMapping();
+@@ -997,7 +1001,7 @@
+                             // each UTF-8 byte as a char
+                             // this is what chrome does
+                             String user3 = new String(b1, "ISO-8859-1");
+-                            realm.putUser(user3, cred, role);
++                            userStore.addUser(user3, cred, role);
+                             constraint = new Constraint(user3, JETTY_ROLE);
+                             constraint.setAuthenticate(true);
+                             cm = new ConstraintMapping();
+@@ -1072,8 +1076,8 @@
+         }
+ 
+         @Override
+-        public UserIdentity login(String username, Object credentials) {
+-            UserIdentity rv = super.login(username, credentials);
++        public UserIdentity login(String username, Object credentials, ServletRequest request) {
++            UserIdentity rv = super.login(username, credentials, request);
+             if (rv == null)
+                 //_log.logAlways(net.i2p.util.Log.WARN, "Console authentication failed, webapp: " + _webapp + ", user: " + username);
+                 _log.logAlways(net.i2p.util.Log.WARN, "Console authentication failed, user: " + username);
+--- a/apps/routerconsole/java/src/net/i2p/router/web/LocaleWebAppHandler.java
++++ b/apps/routerconsole/java/src/net/i2p/router/web/LocaleWebAppHandler.java
+@@ -85,9 +85,9 @@
+                     String testPath = pathInContext.substring(0, len - 4) + '_' + lang + ".jsp";
+                     // Do we have a servlet for the new path that isn't the catchall *.jsp?
+                     @SuppressWarnings("rawtypes")
+-                    Map.Entry servlet = _wac.getServletHandler().getHolderEntry(testPath);
++                    org.eclipse.jetty.http.pathmap.MappedResource servlet = _wac.getServletHandler().getMappedServlet(testPath);
+                     if (servlet != null) {
+-                        String servletPath = (String) servlet.getKey();
++                        String servletPath = servlet.getPathSpec().getDeclaration();
+                         if (servletPath != null && !servletPath.startsWith("*")) {
+                             // success!!
+                             //System.err.println("Servlet is: " + servletPath);
diff --git a/debian-alt/bionic/patches/series b/debian-alt/bionic/patches/series
new file mode 100644
index 0000000000000000000000000000000000000000..5fabbd7e7c0020b71b1289068ed5ebd339359eae
--- /dev/null
+++ b/debian-alt/bionic/patches/series
@@ -0,0 +1,2 @@
+0001-path-substitution.patch
+0002-jetty-compatibility.patch
diff --git a/debian-alt/doc/debian-build.txt b/debian-alt/doc/debian-build.txt
index df9691c1ba0c256643798ba3a1c2a6376ff4d378..d8bedef32c0601de1875b49432bfe33c7b89f84f 100644
--- a/debian-alt/doc/debian-build.txt
+++ b/debian-alt/doc/debian-build.txt
@@ -77,6 +77,7 @@ reprepro -v update xenial
 reprepro -v update bionic
 reprepro -v update cosmic
 reprepro -v update disco
+reprepro -v update eoan
 # todo when set up
 # To add or remove distributions, edit /var/www/debian/conf/distributions
 # and /var/www/debian/conf/updates
@@ -95,7 +96,8 @@ reprepro -v copysrc jessie trusty i2p
 
 # To copy bionic to stretch:
 # Note that syntax is reprepro copysrc TO FROM package!
-reprepro -v copysrc stretch bionic i2p
+TODO bionic (now with jetty 9.4) is no longer compatible with stretch (with jetty 9.2),
+is trusty compatible with stretch?
 
 # To copy disco to sid:
 # Note that syntax is reprepro copysrc TO FROM package!
diff --git a/debian-alt/doc/launchpad.txt b/debian-alt/doc/launchpad.txt
index dc3212c44c22c34b2ab79e5421e6dd4fcb08736d..d1f5580e7a280e162128879f8ad82d4c1062f136 100644
--- a/debian-alt/doc/launchpad.txt
+++ b/debian-alt/doc/launchpad.txt
@@ -17,8 +17,9 @@ Min version	Max version	Uses src pkg	Copied to Debian
 precise		saucy   	precise		wheezy
 trusty		wily		precise		jessie
 xenial		yakkety		xenial		--
-bionic		cosmic		bionic		stretch
-disco		disco		bionic		buster, sid
+--		--		--		stretch
+bionic		cosmic		bionic		--
+disco		eoan		bionic		buster, sid
 
 
 Prep (first time only)