diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 570db05871..72e9751294 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -368,11 +368,11 @@ void DomainServer::setupNodeListAndAssignments() {
     const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port";
 
     QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
-    unsigned short domainServerPort = (unsigned short) localPortValue.toUInt();
+    int domainServerPort = localPortValue.toInt();
 
     QVariantMap& settingsMap = _settingsManager.getSettingsMap();
 
-    unsigned short domainServerDTLSPort = 0;
+    int domainServerDTLSPort = INVALID_PORT;
 
     if (_isUsingDTLS) {
         domainServerDTLSPort = DEFAULT_DOMAIN_SERVER_DTLS_PORT;
diff --git a/interface/resources/qml/hifi/dialogs/NetworkingPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/NetworkingPreferencesDialog.qml
new file mode 100644
index 0000000000..652d3347fc
--- /dev/null
+++ b/interface/resources/qml/hifi/dialogs/NetworkingPreferencesDialog.qml
@@ -0,0 +1,19 @@
+import QtQuick 2.5
+import Qt.labs.settings 1.0
+
+import "../../dialogs"
+
+PreferencesDialog {
+    id: root
+    objectName: "NetworkingPreferencesDialog"
+    title: "Networking Settings"
+    showCategories: ["Networking"]
+    property var settings: Settings {
+        category: root.objectName
+        property alias x: root.x
+        property alias y: root.y
+        property alias width: root.width
+        property alias height: root.height
+    }
+}
+
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 8620b384ec..904a6c5b65 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -400,12 +400,10 @@ static const QString STATE_GROUNDED = "Grounded";
 static const QString STATE_NAV_FOCUSED = "NavigationFocused";
 
 bool setupEssentials(int& argc, char** argv) {
-    unsigned int listenPort = 0; // bind to an ephemeral port by default
     const char** constArgv = const_cast<const char**>(argv);
     const char* portStr = getCmdOption(argc, constArgv, "--listenPort");
-    if (portStr) {
-        listenPort = atoi(portStr);
-    }
+    const int listenPort = portStr ? atoi(portStr) : INVALID_PORT;
+
     // Set build version
     QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
 
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 50dc748461..d3caa4a092 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -522,6 +522,11 @@ Menu::Menu() {
 
     // Developer > Network >>>
     MenuWrapper* networkMenu = developerMenu->addMenu("Network");
+    action = addActionToQMenuAndActionHash(networkMenu, MenuOption::Networking);
+    connect(action, &QAction::triggered, [] {
+        DependencyManager::get<OffscreenUi>()->toggle(QUrl("hifi/dialogs/NetworkingPreferencesDialog.qml"),
+                                                      "NetworkingPreferencesDialog");
+    });
     addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
     addCheckableActionToQMenuAndActionHash(networkMenu,
         MenuOption::DisableActivityLogger,
diff --git a/interface/src/Menu.h b/interface/src/Menu.h
index ee00644746..b25603caeb 100644
--- a/interface/src/Menu.h
+++ b/interface/src/Menu.h
@@ -129,6 +129,7 @@ namespace MenuOption {
     const QString MuteEnvironment = "Mute Environment";
     const QString MuteFaceTracking = "Mute Face Tracking";
     const QString NamesAboveHeads = "Names Above Heads";
+    const QString Networking = "Networking...";
     const QString NoFaceTracking = "None";
     const QString OctreeStats = "Entity Statistics";
     const QString OnePointCalibration = "1 Point Calibration";
diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp
index 7fdafc9bda..0ba057a5e3 100644
--- a/interface/src/ui/PreferencesDialog.cpp
+++ b/interface/src/ui/PreferencesDialog.cpp
@@ -332,4 +332,19 @@ void setupPreferences() {
             preferences->addPreference(preference);
         }
     }
+    {
+        static const QString RENDER("Networking");
+
+        auto nodelist = DependencyManager::get<NodeList>();
+        {
+            static const int MIN_PORT_NUMBER { 0 };
+            static const int MAX_PORT_NUMBER { 65535 };
+            auto getter = [nodelist] { return static_cast<int>(nodelist->getSocketLocalPort()); };
+            auto setter = [nodelist](int preset) { nodelist->setSocketLocalPort(static_cast<quint16>(preset)); };
+            auto preference = new IntSpinnerPreference(RENDER, "Listening Port", getter, setter);
+            preference->setMin(MIN_PORT_NUMBER);
+            preference->setMax(MAX_PORT_NUMBER);
+            preferences->addPreference(preference);
+        }
+    }
 }
diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp
index 6d9de2dbc1..7fad66d608 100644
--- a/libraries/networking/src/LimitedNodeList.cpp
+++ b/libraries/networking/src/LimitedNodeList.cpp
@@ -18,6 +18,7 @@
 #include <QtCore/QDataStream>
 #include <QtCore/QDebug>
 #include <QtCore/QJsonDocument>
+#include <QtCore/QThread>
 #include <QtCore/QUrl>
 #include <QtNetwork/QHostInfo>
 
@@ -25,6 +26,7 @@
 
 #include <LogHandler.h>
 #include <NumericalConstants.h>
+#include <SettingHandle.h>
 #include <SharedUtil.h>
 #include <UUID.h>
 
@@ -34,12 +36,14 @@
 #include "NetworkLogging.h"
 #include "udt/Packet.h"
 
+static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0);
+
 const std::set<NodeType_t> SOLO_NODE_TYPES = {
     NodeType::AvatarMixer,
     NodeType::AudioMixer
 };
 
-LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort) :
+LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
     _sessionUUID(),
     _nodeHash(),
     _nodeMutex(QReadWriteLock::Recursive),
@@ -62,11 +66,11 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
     }
 
     qRegisterMetaType<ConnectionStep>("ConnectionStep");
-
-    _nodeSocket.bind(QHostAddress::AnyIPv4, socketListenPort);
+    auto port = (socketListenPort != INVALID_PORT) ? socketListenPort : LIMITED_NODELIST_LOCAL_PORT.get();
+    _nodeSocket.bind(QHostAddress::AnyIPv4, port);
     qCDebug(networking) << "NodeList socket is listening on" << _nodeSocket.localPort();
 
-    if (dtlsListenPort > 0) {
+    if (dtlsListenPort != INVALID_PORT) {
         // only create the DTLS socket during constructor if a custom port is passed
         _dtlsSocket = new QUdpSocket(this);
 
@@ -157,6 +161,18 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) {
     }
 }
 
+void LimitedNodeList::setSocketLocalPort(quint16 socketLocalPort) {
+    if (QThread::currentThread() != thread()) {
+        QMetaObject::invokeMethod(this, "setSocketLocalPort", Qt::QueuedConnection,
+                                  Q_ARG(quint16, socketLocalPort));
+        return;
+    }
+    if (_nodeSocket.localPort() != socketLocalPort) {
+        _nodeSocket.rebind(socketLocalPort);
+        LIMITED_NODELIST_LOCAL_PORT.set(socketLocalPort);
+    }
+}
+
 QUdpSocket& LimitedNodeList::getDTLSSocket() {
     if (!_dtlsSocket) {
         // DTLS socket getter called but no DTLS socket exists, create it now
@@ -606,6 +622,12 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
             });
         }
 
+        // Signal when a socket changes, so we can start the hole punch over.
+        auto weakPtr = newNodePointer.toWeakRef(); // We don't want the lambda to hold a strong ref
+        connect(newNodePointer.data(), &NetworkPeer::socketUpdated, this, [=] {
+            emit nodeSocketUpdated(weakPtr);
+        });
+
         return newNodePointer;
     }
 }
diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h
index 49a3a155a2..cd343a5232 100644
--- a/libraries/networking/src/LimitedNodeList.h
+++ b/libraries/networking/src/LimitedNodeList.h
@@ -45,6 +45,8 @@
 #include "udt/Socket.h"
 #include "UUIDHasher.h"
 
+const int INVALID_PORT = -1;
+
 const quint64 NODE_SILENCE_THRESHOLD_MSECS = 5 * 1000;
 
 extern const std::set<NodeType_t> SOLO_NODE_TYPES;
@@ -113,6 +115,8 @@ public:
     bool getThisNodeCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); }
 
     quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); }
+    Q_INVOKABLE void setSocketLocalPort(quint16 socketLocalPort);
+
     QUdpSocket& getDTLSSocket();
 
     PacketReceiver& getPacketReceiver() { return *_packetReceiver; }
@@ -250,6 +254,7 @@ signals:
 
     void uuidChanged(const QUuid& ownerUUID, const QUuid& oldUUID);
     void nodeAdded(SharedNodePointer);
+    void nodeSocketUpdated(SharedNodePointer);
     void nodeKilled(SharedNodePointer);
     void nodeActivated(SharedNodePointer);
 
@@ -267,9 +272,9 @@ protected slots:
     void errorTestingLocalSocket();
 
 protected:
-    LimitedNodeList(unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
-    LimitedNodeList(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
-    void operator=(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
+    LimitedNodeList(int socketListenPort = INVALID_PORT, int dtlsListenPort = INVALID_PORT);
+    LimitedNodeList(LimitedNodeList const&) = delete; // Don't implement, needed to avoid copies of singleton
+    void operator=(LimitedNodeList const&) = delete; // Don't implement, needed to avoid copies of singleton
     
     qint64 sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode,
                       const HifiSockAddr& overridenSockAddr);
diff --git a/libraries/networking/src/NetworkPeer.cpp b/libraries/networking/src/NetworkPeer.cpp
index da2eced05c..2ebee1b71f 100644
--- a/libraries/networking/src/NetworkPeer.cpp
+++ b/libraries/networking/src/NetworkPeer.cpp
@@ -63,6 +63,7 @@ void NetworkPeer::setPublicSocket(const HifiSockAddr& publicSocket) {
         
         if (!wasOldSocketNull) {
             qCDebug(networking) << "Public socket change for node" << *this;
+            emit socketUpdated();
         }
     }
 }
@@ -82,6 +83,7 @@ void NetworkPeer::setLocalSocket(const HifiSockAddr& localSocket) {
 
         if (!wasOldSocketNull) {
             qCDebug(networking) << "Local socket change for node" << *this;
+            emit socketUpdated();
         }
     }
 }
@@ -101,6 +103,7 @@ void NetworkPeer::setSymmetricSocket(const HifiSockAddr& symmetricSocket) {
         
         if (!wasOldSocketNull) {
             qCDebug(networking) << "Symmetric socket change for node" << *this;
+            emit socketUpdated();
         }
     }
 }
diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h
index 8298a2dad4..7185ffef29 100644
--- a/libraries/networking/src/NetworkPeer.h
+++ b/libraries/networking/src/NetworkPeer.h
@@ -81,9 +81,12 @@ public:
 public slots:
     void startPingTimer();
     void stopPingTimer();
+
 signals:
     void pingTimerTimeout();
     void socketActivated(const HifiSockAddr& sockAddr);
+    void socketUpdated();
+
 protected:
     void setActiveSocket(HifiSockAddr* discoveredSocket);
 
diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp
index 3a07ea8b54..593a79b311 100644
--- a/libraries/networking/src/NodeList.cpp
+++ b/libraries/networking/src/NodeList.cpp
@@ -33,7 +33,7 @@
 
 const int KEEPALIVE_PING_INTERVAL_MS = 1000;
 
-NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned short dtlsListenPort) :
+NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) :
     LimitedNodeList(socketListenPort, dtlsListenPort),
     _ownerType(newOwnerType),
     _nodeTypesOfInterest(),
@@ -93,6 +93,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
 
     // anytime we get a new node we will want to attempt to punch to it
     connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch);
+    connect(this, &LimitedNodeList::nodeSocketUpdated, this, &NodeList::startNodeHolePunch);
 
     // anytime we get a new node we may need to re-send our set of ignored node IDs to it
     connect(this, &LimitedNodeList::nodeActivated, this, &NodeList::maybeSendIgnoreSetToNode);
diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h
index f3cd5bed0d..f08c0dbe45 100644
--- a/libraries/networking/src/NodeList.h
+++ b/libraries/networking/src/NodeList.h
@@ -116,10 +116,10 @@ private slots:
     void maybeSendIgnoreSetToNode(SharedNodePointer node);
     
 private:
-    NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile
-    NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
-    NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton
-    void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton
+    NodeList() : LimitedNodeList(INVALID_PORT, INVALID_PORT) { assert(false); } // Not implemented, needed for DependencyManager templates compile
+    NodeList(char ownerType, int socketListenPort = INVALID_PORT, int dtlsListenPort = INVALID_PORT);
+    NodeList(NodeList const&) = delete; // Don't implement, needed to avoid copies of singleton
+    void operator=(NodeList const&) = delete; // Don't implement, needed to avoid copies of singleton
 
     void processDomainServerAuthRequest(const QByteArray& packet);
     void requestAuthForDomainServer();
diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp
index a39100f8d1..37ededa55c 100644
--- a/libraries/networking/src/udt/Socket.cpp
+++ b/libraries/networking/src/udt/Socket.cpp
@@ -63,10 +63,12 @@ void Socket::bind(const QHostAddress& address, quint16 port) {
 }
 
 void Socket::rebind() {
-    quint16 oldPort = _udpSocket.localPort();
+    rebind(_udpSocket.localPort());
+}
 
+void Socket::rebind(quint16 localPort) {
     _udpSocket.close();
-    bind(QHostAddress::AnyIPv4, oldPort);
+    bind(QHostAddress::AnyIPv4, localPort);
 }
 
 void Socket::setSystemBufferSizes() {
diff --git a/libraries/networking/src/udt/Socket.h b/libraries/networking/src/udt/Socket.h
index 6b8ccf1fa8..bc4393d4bd 100644
--- a/libraries/networking/src/udt/Socket.h
+++ b/libraries/networking/src/udt/Socket.h
@@ -61,8 +61,9 @@ public:
     qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& sockAddr);
     
     void bind(const QHostAddress& address, quint16 port = 0);
+    void rebind(quint16 port);
     void rebind();
-    
+
     void setPacketFilterOperator(PacketFilterOperator filterOperator) { _packetFilterOperator = filterOperator; }
     void setPacketHandler(PacketHandler handler) { _packetHandler = handler; }
     void setMessageHandler(MessageHandler handler) { _messageHandler = handler; }
diff --git a/libraries/shared/src/Preferences.h b/libraries/shared/src/Preferences.h
index abb3f5afbd..f1915a9d6a 100644
--- a/libraries/shared/src/Preferences.h
+++ b/libraries/shared/src/Preferences.h
@@ -189,6 +189,38 @@ protected:
     float _step { 0.1f };
 };
 
+
+class IntPreference : public TypedPreference<int> {
+    Q_OBJECT
+    Q_PROPERTY(float value READ getValue WRITE setValue NOTIFY valueChanged)
+    Q_PROPERTY(float min READ getMin CONSTANT)
+    Q_PROPERTY(float max READ getMax CONSTANT)
+    Q_PROPERTY(float step READ getStep CONSTANT)
+
+public:
+    IntPreference(const QString& category, const QString& name, Getter getter, Setter setter)
+        : TypedPreference(category, name, getter, setter) { }
+
+    float getMin() const { return _min; }
+    void setMin(float min) { _min = min; };
+
+    float getMax() const { return _max; }
+    void setMax(float max) { _max = max; };
+
+    float getStep() const { return _step; }
+    void setStep(float step) { _step = step; };
+
+signals:
+    void valueChanged();
+
+protected:
+    void emitValueChanged() override { emit valueChanged(); }
+
+    int _min { std::numeric_limits<int>::min() };
+    int _max { std::numeric_limits<int>::max() };
+    int _step { 1 };
+};
+
 class StringPreference : public TypedPreference<QString> {
     Q_OBJECT
     Q_PROPERTY(QString value READ getValue WRITE setValue NOTIFY valueChanged)
@@ -222,6 +254,15 @@ public:
     Type getType() override { return Spinner; }
 };
 
+class IntSpinnerPreference : public IntPreference {
+    Q_OBJECT
+public:
+    IntSpinnerPreference(const QString& category, const QString& name, Getter getter, Setter setter)
+        : IntPreference(category, name, getter, setter) { }
+
+    Type getType() override { return Spinner; }
+};
+
 class EditPreference : public StringPreference {
     Q_OBJECT
     Q_PROPERTY(QString placeholderText READ getPlaceholderText CONSTANT)
diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp
index c6cca74c69..7fa36136d2 100644
--- a/tests/render-perf/src/main.cpp
+++ b/tests/render-perf/src/main.cpp
@@ -503,7 +503,7 @@ public:
         DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
         DependencyManager::registerInheritance<SpatialParentFinder, ParentFinder>();
         DependencyManager::set<AddressManager>();
-        DependencyManager::set<NodeList>(NodeType::Agent, 0);
+        DependencyManager::set<NodeList>(NodeType::Agent);
         DependencyManager::set<DeferredLightingEffect>();
         DependencyManager::set<ResourceCacheSharedItems>();
         DependencyManager::set<TextureCache>();
diff --git a/tests/render-texture-load/src/main.cpp b/tests/render-texture-load/src/main.cpp
index fd6885c381..04bce72757 100644
--- a/tests/render-texture-load/src/main.cpp
+++ b/tests/render-texture-load/src/main.cpp
@@ -295,7 +295,7 @@ public:
         DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
         //DependencyManager::registerInheritance<SpatialParentFinder, ParentFinder>();
         DependencyManager::set<AddressManager>();
-        DependencyManager::set<NodeList>(NodeType::Agent, 0);
+        DependencyManager::set<NodeList>(NodeType::Agent);
         DependencyManager::set<DeferredLightingEffect>();
         DependencyManager::set<ResourceCacheSharedItems>();
         DependencyManager::set<TextureCache>();