diff --git a/app/api/network.py b/app/api/network.py
index 73c98c702b7c3f60e57f19667a3f44ca4ff1eb8e..2c8b3d4e2a58117dde385a51bcdad69b46603aec 100644
--- a/app/api/network.py
+++ b/app/api/network.py
@@ -73,6 +73,7 @@ def create_network():
     :jsonparam address: vlan address
     :jsonparam first_ip: first IP of the allowed range
     :jsonparam last_ip: last IP of the allowed range
+    :jsonparam gateway: gateway IP
     :jsonparam scope: network scope name
     :jsonparam domain_id: (optional) primary key of the domain [default: scope domain]
     :jsonparam admin_only: (optional) boolean to restrict the network to admin users [default: False]
@@ -87,6 +88,7 @@ def create_network():
             "address",
             "first_ip",
             "last_ip",
+            "gateway",
             "scope",
         ),
     )
diff --git a/app/models.py b/app/models.py
index 41a0ed6729287b2cc4d2addf67b36a1fa00169f9..c766762e6cd140ca463ba0418f088cda2a933f61 100644
--- a/app/models.py
+++ b/app/models.py
@@ -759,6 +759,7 @@ class Network(CreatedMixin, db.Model):
     address = db.Column(postgresql.CIDR, nullable=False, unique=True)
     first_ip = db.Column(postgresql.INET, nullable=False, unique=True)
     last_ip = db.Column(postgresql.INET, nullable=False, unique=True)
+    gateway = db.Column(postgresql.INET, nullable=False, unique=True)
     description = db.Column(db.Text)
     admin_only = db.Column(db.Boolean, nullable=False, default=False)
     scope_id = db.Column(db.Integer, db.ForeignKey("network_scope.id"), nullable=False)
@@ -772,6 +773,7 @@ class Network(CreatedMixin, db.Model):
         sa.CheckConstraint("first_ip < last_ip", name="first_ip_less_than_last_ip"),
         sa.CheckConstraint("first_ip << address", name="first_ip_in_network"),
         sa.CheckConstraint("last_ip << address", name="last_ip_in_network"),
+        sa.CheckConstraint("gateway << address", name="gateway_in_network"),
     )
 
     def __init__(self, **kwargs):
@@ -825,11 +827,6 @@ class Network(CreatedMixin, db.Model):
         """Return the list of IP addresses available"""
         return [addr for addr in self.ip_range() if addr not in self.used_ips()]
 
-    @property
-    def gateway(self):
-        """Return the network gateway IP"""
-        return list(self.network_ip.hosts())[-1]
-
     @staticmethod
     def ip_in_network(ip, address):
         """Ensure the IP is in the network
@@ -897,7 +894,7 @@ class Network(CreatedMixin, db.Model):
                 "netmask": str(self.netmask),
                 "first_ip": self.first_ip,
                 "last_ip": self.last_ip,
-                "gateway": str(self.gateway),
+                "gateway": self.gateway,
                 "description": self.description,
                 "admin_only": self.admin_only,
                 "scope": utils.format_field(self.scope),
diff --git a/app/network/forms.py b/app/network/forms.py
index ab58a88308a8142737f730fb8f9b162b326430cb..fad2c3c44ccb1d1f3b3e46011ac10903ec667a50 100644
--- a/app/network/forms.py
+++ b/app/network/forms.py
@@ -117,6 +117,7 @@ class NetworkForm(CSEntryForm):
     address = NoValidateSelectField("Address", choices=[])
     first_ip = NoValidateSelectField("First IP", choices=[])
     last_ip = NoValidateSelectField("Last IP", choices=[])
+    gateway = NoValidateSelectField("Gateway IP", choices=[])
     domain_id = SelectField("Domain")
     admin_only = BooleanField("Admin only")
 
diff --git a/app/network/views.py b/app/network/views.py
index f69ff4319c49d25f97e3a679f4dd58688a18b6c2..8705dcaf326f2891c9f1413b05fb8efb3602edb9 100644
--- a/app/network/views.py
+++ b/app/network/views.py
@@ -579,27 +579,8 @@ def retrieve_first_available_ip(network_id):
 @bp.route("/networks")
 @login_required
 def list_networks():
-    return render_template("network/networks.html")
-
-
-@bp.route("/_retrieve_networks")
-@login_required
-def retrieve_networks():
-    data = [
-        (
-            str(network.scope),
-            network.vlan_name,
-            network.vlan_id,
-            network.description,
-            network.address,
-            network.first_ip,
-            network.last_ip,
-            str(network.domain),
-            network.admin_only,
-        )
-        for network in models.Network.query.all()
-    ]
-    return jsonify(data=data)
+    networks = models.Network.query.all()
+    return render_template("network/networks.html", networks=networks)
 
 
 @bp.route("/networks/create", methods=("GET", "POST"))
@@ -624,6 +605,7 @@ def create_network():
             address=form.address.data,
             first_ip=form.first_ip.data,
             last_ip=form.last_ip.data,
+            gateway=form.gateway.data,
             domain=models.Domain.query.get(form.domain_id.data),
             admin_only=form.admin_only.data,
         )
@@ -696,16 +678,28 @@ def retrieve_ips(subnet, prefix):
         address = ipaddress.ip_network(f"{subnet}/{prefix}")
     except ValueError:
         current_app.logger.warning(f"Invalid address: {subnet}/{prefix}")
-        data = {"ips": [], "first": "", "last": ""}
+        data = {
+            "ips": [],
+            "selected_first": "",
+            "selected_last": "",
+            "selected_gateway": "",
+        }
     else:
         hosts = [str(ip) for ip in address.hosts()]
+        # The gateway is set to the last IP by default
+        gateway = hosts[-1]
         if len(hosts) > 17:
             first = hosts[10]
             last = hosts[-6]
         else:
             first = hosts[0]
-            last = hosts[-1]
-        data = {"ips": hosts, "selected_first": first, "selected_last": last}
+            last = hosts[-2]
+        data = {
+            "ips": hosts,
+            "selected_first": first,
+            "selected_last": last,
+            "selected_gateway": gateway,
+        }
     return jsonify(data=data)
 
 
diff --git a/app/static/js/networks.js b/app/static/js/networks.js
index 986801fe64df6a61d286e6f6e81e3268297ca98c..58d1b1cbffeaf8cecd66569452346bcc1e4c44a2 100644
--- a/app/static/js/networks.js
+++ b/app/static/js/networks.js
@@ -24,12 +24,12 @@ $(document).ready(function() {
       $SCRIPT_ROOT + "/network/_retrieve_subnets/" + scope_id + "/" + prefix,
       function(json) {
         update_selectfield("#address", json.data.subnets, json.data.selected_subnet);
-        update_first_and_last_ip();
+        update_gateway_first_and_last_ip();
       }
     );
   }
 
-  function update_first_and_last_ip() {
+  function update_gateway_first_and_last_ip() {
     // Retrieve IPs for the selected subnet
     // and update the first and last ip select field
     var address = $("#address").val();
@@ -38,6 +38,7 @@ $(document).ready(function() {
       function(json) {
         update_selectfield("#first_ip", json.data.ips, json.data.selected_first);
         update_selectfield("#last_ip", json.data.ips.slice().reverse(), json.data.selected_last);
+        update_selectfield("#gateway", json.data.ips, json.data.selected_gateway);
       }
     );
   }
@@ -59,17 +60,10 @@ $(document).ready(function() {
 
   // Update first and last ip select field when changing address
   $("#address").on('change', function() {
-    update_first_and_last_ip();
+    update_gateway_first_and_last_ip();
   });
 
   var networks_table =  $("#networks_table").DataTable({
-    "ajax": function(data, callback, settings) {
-      $.getJSON(
-        $SCRIPT_ROOT + "/network/_retrieve_networks",
-        function(json) {
-          callback(json);
-        });
-    },
     "paging": false
   });
 
diff --git a/app/templates/network/create_network.html b/app/templates/network/create_network.html
index 506eb1f88143f19e835f9ff469f717d33b5d5b6b..72cd6daec403f57df7aaf240d81c496f307a07be 100644
--- a/app/templates/network/create_network.html
+++ b/app/templates/network/create_network.html
@@ -14,6 +14,7 @@
     {{ render_field(form.address) }}
     {{ render_field(form.first_ip) }}
     {{ render_field(form.last_ip) }}
+    {{ render_field(form.gateway) }}
     {{ render_field(form.domain_id) }}
     {{ render_field(form.admin_only) }}
     <div class="form-group row">
diff --git a/app/templates/network/networks.html b/app/templates/network/networks.html
index 0ab11ff0a1fe869e6b9935ba8c34409ba543fc53..f28dd3ae0f797fb0bd4035e0a556aa2241bf2b70 100644
--- a/app/templates/network/networks.html
+++ b/app/templates/network/networks.html
@@ -21,17 +21,34 @@
   <table id="networks_table" class="table table-bordered table-hover table-sm" cellspacing="0" width="100%">
     <thead>
       <tr>
-        <th>Network scope</th>
         <th>Vlan name</th>
         <th>Vlan id</th>
         <th>Description</th>
         <th>Address</th>
         <th>First IP</th>
         <th>Last IP</th>
+        <th>Gateway</th>
+        <th>Network scope</th>
         <th>Domain</th>
         <th>Admin only</th>
       </tr>
     </thead>
+    <tbody>
+      {% for network in networks %}
+        <tr>
+          <td>{{ network.vlan_name }}</td>
+          <td>{{ network.vlan_id }}</td>
+          <td>{{ network.description }}</td>
+          <td>{{ network.address }}</td>
+          <td>{{ network.first_ip }}</td>
+          <td>{{ network.last_ip }}</td>
+          <td>{{ network.gateway }}</td>
+          <td>{{ network.scope }}</td>
+          <td>{{ network.domain }}</td>
+          <td>{{ network.admin_only }}</td>
+        </tr>
+      {% endfor %}
+    </tbody>
   </table>
   {%- endblock %}
 {%- endblock %}
diff --git a/migrations/versions/f7d72e432f51_add_gateway_field.py b/migrations/versions/f7d72e432f51_add_gateway_field.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f87d594d83c6a9f8f64e8a64daea11139976367
--- /dev/null
+++ b/migrations/versions/f7d72e432f51_add_gateway_field.py
@@ -0,0 +1,50 @@
+"""Add gateway field
+
+Revision ID: f7d72e432f51
+Revises: 7c38e78b6de6
+Create Date: 2019-02-27 17:35:22.535126
+
+"""
+import ipaddress
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = "f7d72e432f51"
+down_revision = "7c38e78b6de6"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    op.add_column("network", sa.Column("gateway", postgresql.INET(), nullable=True))
+    network = sa.sql.table(
+        "network",
+        sa.sql.column("id"),
+        sa.sql.column("address"),
+        sa.sql.column("gateway"),
+    )
+    # Fill the gateway based on the network address
+    conn = op.get_bind()
+    res = conn.execute("SELECT id, address FROM network")
+    results = res.fetchall()
+    for result in results:
+        address = ipaddress.ip_network(result[1])
+        hosts = list(address.hosts())
+        # Use last IP by default
+        gateway = str(hosts[-1])
+        op.execute(
+            network.update().where(network.c.id == result[0]).values(gateway=gateway)
+        )
+    op.create_check_constraint(
+        op.f("ck_network_gateway_in_network"), "network", "gateway << address"
+    )
+    op.create_unique_constraint(op.f("uq_network_gateway"), "network", ["gateway"])
+    op.alter_column("network", "gateway", nullable=False)
+
+
+def downgrade():
+    op.drop_constraint(op.f("uq_network_gateway"), "network", type_="unique")
+    op.drop_constraint(op.f("ck_network_gateway_in_network"), "network", type_="check")
+    op.drop_column("network", "gateway")
diff --git a/tests/functional/factories.py b/tests/functional/factories.py
index cd1fdc1806b99c1d55fa7a46658a36281021130f..72fa05b748b5ed4102da790cef1312422b007b0b 100644
--- a/tests/functional/factories.py
+++ b/tests/functional/factories.py
@@ -144,6 +144,12 @@ class NetworkFactory(factory.alchemy.SQLAlchemyModelFactory):
         hosts = list(net.hosts())
         return str(hosts[-5])
 
+    @factory.lazy_attribute
+    def gateway(self):
+        net = ipaddress.ip_network(self.address)
+        hosts = list(net.hosts())
+        return str(hosts[-1])
+
 
 class DeviceTypeFactory(factory.alchemy.SQLAlchemyModelFactory):
     class Meta:
diff --git a/tests/functional/test_api.py b/tests/functional/test_api.py
index 1c2960714f106f8f68b706ee1b097c961427322a..294820165e8880b4f55fcad54a22d5f480dbb450 100644
--- a/tests/functional/test_api.py
+++ b/tests/functional/test_api.py
@@ -649,7 +649,7 @@ def test_create_network_auth_fail(client, session, user_token):
 
 def test_create_network(client, admin_token, network_scope_factory):
     scope = network_scope_factory(supernet="172.16.0.0/16")
-    # check that vlan_name, vlan_id, address, first_ip, last_ip and scope are mandatory
+    # check that vlan_name, vlan_id, address, first_ip, last_ip, gateway and scope are mandatory
     response = post(client, f"{API_URL}/network/networks", data={}, token=admin_token)
     check_response_message(response, "Missing mandatory field 'vlan_name'", 422)
     response = post(
@@ -692,6 +692,20 @@ def test_create_network(client, admin_token, network_scope_factory):
         token=admin_token,
     )
     check_response_message(response, "Missing mandatory field 'last_ip'", 422)
+    response = post(
+        client,
+        f"{API_URL}/network/networks",
+        data={
+            "vlan_name": "network1",
+            "vlan_id": 1600,
+            "address": "172.16.1.0/24",
+            "first_ip": "172.16.1.10",
+            "last_ip": "172.16.1.250",
+            "scope": scope.name,
+        },
+        token=admin_token,
+    )
+    check_response_message(response, "Missing mandatory field 'gateway'", 422)
 
     data = {
         "vlan_name": "network1",
@@ -699,6 +713,7 @@ def test_create_network(client, admin_token, network_scope_factory):
         "address": "172.16.1.0/24",
         "first_ip": "172.16.1.10",
         "last_ip": "172.16.1.250",
+        "gateway": "172.16.1.254",
         "scope": scope.name,
     }
     response = post(client, f"{API_URL}/network/networks", data=data, token=admin_token)
@@ -726,6 +741,7 @@ def test_create_network(client, admin_token, network_scope_factory):
     assert response.get_json()["address"] == "172.16.1.0/24"
     assert response.get_json()["first_ip"] == "172.16.1.10"
     assert response.get_json()["last_ip"] == "172.16.1.250"
+    assert response.get_json()["gateway"] == "172.16.1.254"
     assert response.get_json()["netmask"] == "255.255.255.0"
 
     # Check that address and name shall be unique
@@ -751,6 +767,7 @@ def test_create_network(client, admin_token, network_scope_factory):
         "address": "172.16.2.0/24",
         "first_ip": "172.16.2.10",
         "last_ip": "172.16.2.250",
+        "gateway": "172.16.2.254",
         "scope": scope.name,
     }
     response = post(
@@ -769,6 +786,7 @@ def test_create_network(client, admin_token, network_scope_factory):
         "address": "172.16.5.0/24",
         "first_ip": "172.16.5.11",
         "last_ip": "172.16.5.250",
+        "gateway": "172.16.5.254",
         "description": "long description",
         "scope": scope.name,
     }
@@ -790,6 +808,7 @@ def test_create_network_invalid_address(client, admin_token, network_scope):
         "address": "foo",
         "first_ip": "172.16.1.10",
         "last_ip": "172.16.1.250",
+        "gateway": "172.16.1.254",
         "scope": network_scope.name,
     }
     response = post(client, f"{API_URL}/network/networks", data=data, token=admin_token)
@@ -818,6 +837,7 @@ def test_create_network_invalid_ip(
         "address": "192.168.0.0/24",
         "first_ip": address,
         "last_ip": "192.168.0.250",
+        "gateway": "192.168.0.254",
         "scope": network_scope.name,
     }
     response = post(client, f"{API_URL}/network/networks", data=data, token=admin_token)
@@ -831,6 +851,7 @@ def test_create_network_invalid_ip(
         "address": "192.168.0.0/24",
         "first_ip": "192.168.0.250",
         "last_ip": address,
+        "gateway": "192.168.0.254",
         "scope": network_scope.name,
     }
     response = post(client, f"{API_URL}/network/networks", data=data, token=admin_token)
@@ -847,6 +868,7 @@ def test_create_network_invalid_range(client, session, admin_token, network_scop
         "address": "172.16.1.0/24",
         "first_ip": "172.16.2.10",
         "last_ip": "172.16.1.250",
+        "gateway": "172.16.1.254",
         "scope": network_scope.name,
     }
     response = post(client, f"{API_URL}/network/networks", data=data, token=admin_token)
@@ -860,6 +882,7 @@ def test_create_network_invalid_range(client, session, admin_token, network_scop
         "address": "172.16.1.0/24",
         "first_ip": "172.16.1.10",
         "last_ip": "172.16.5.250",
+        "gateway": "172.16.1.1",
         "scope": network_scope.name,
     }
     response = post(client, f"{API_URL}/network/networks", data=data, token=admin_token)
@@ -873,6 +896,7 @@ def test_create_network_invalid_range(client, session, admin_token, network_scop
         "address": "172.16.1.0/24",
         "first_ip": "172.16.1.10",
         "last_ip": "172.16.1.9",
+        "gateway": "172.16.1.1",
         "scope": network_scope.name,
     }
     response = post(client, f"{API_URL}/network/networks", data=data, token=admin_token)
diff --git a/tests/functional/test_models.py b/tests/functional/test_models.py
index a35d2633877fffaaa9f84134d19abbfc2c7e14c6..18b55dd5ee89d76b63b40bcc1773451f0def405f 100644
--- a/tests/functional/test_models.py
+++ b/tests/functional/test_models.py
@@ -152,9 +152,9 @@ def test_network_available_and_used_ips(network_factory, interface_factory):
 
 
 def test_network_gateway(network_factory):
-    network = network_factory(address="192.168.0.0/24")
-    assert str(network.gateway) == "192.168.0.254"
-    network = network_factory(address="172.16.110.0/23")
+    network = network_factory(address="192.168.0.0/24", gateway="192.168.0.1")
+    assert str(network.gateway) == "192.168.0.1"
+    network = network_factory(address="172.16.110.0/23", gateway="172.16.111.254")
     assert str(network.gateway) == "172.16.111.254"