diff --git a/gso/services/_ipam.py b/gso/services/_ipam.py
index efd1da454f0c853278d6fe4fa52fcede49751a58..b8aef94b3baabee92f2d0972359da63d340b5039 100644
--- a/gso/services/_ipam.py
+++ b/gso/services/_ipam.py
@@ -37,6 +37,8 @@ class HostAddresses(BaseSettings):
 class IPAMErrors(Enum):
     # HTTP error code, match in error message
     CONTAINER_FULL = 400, "Can not find requested number of networks"
+    NETWORK_FULL = 400, \
+        "Cannot find 1 available IP address(es) in this network"
     EXTATTR_UNKNOWN = 400, "Unknown extensible attribute"
     EXTATTR_BADVALUE = 400, "Bad value for extensible attribute"
 
@@ -121,6 +123,10 @@ def _allocate_network(
     ip_container = 'networkcontainer' if ip_version == 4 else \
         'ipv6networkcontainer'
 
+    assert network_params.containers, \
+        "No containers available to allocate networks for this service." \
+        "Maybe you want to allocate a host from a network directly?"
+
     # only return in the response the allocated network, not all available
     # TODO: any validation needed for extrattrs wherever it's used?
     req_payload = {
@@ -206,13 +212,22 @@ def allocate_service_ipv6_network(service_type, comment="", extattrs={}
 
 
 def _find_next_available_ip(infoblox_params, network_ref):
+    """
+    Find the next available IP address from a network given its ref.
+    Returns "NETWORK_FULL" if there's no space in the network.
+    Otherwise returns the next available IP address in the network.
+    """
     r = requests.post(
         f'{_wapi(infoblox_params)}/{network_ref}?_function=next_available_ip&num=1',   # noqa: E501
         auth=HTTPBasicAuth(infoblox_params.username,
                            infoblox_params.password),
         verify=False
     )
-    # TODO: propagate no more available IPs in the network
+
+    if _match_error_code(response=r,
+                         error_code=IPAMErrors.NETWORK_FULL):
+        return "NETWORK_FULL"
+
     assert r.status_code >= 200 and r.status_code < 300, \
         f"HTTP error {r.status_code}: {r.reason}\n\n{r.text}"
     assert 'ips' in r.json()
@@ -226,11 +241,12 @@ def _allocate_host(hostname=None,
                    networks=None,
                    cname_aliases=None,
                    extattrs={}
-                   ) -> HostAddresses:
+                   ) -> Union[HostAddresses, str]:
     """
     If networks is not None, allocate host in those networks.
     Otherwise if addrs is not None, allocate host with those addresses.
     hostname parameter must be full name including domain name.
+    Return an error string if couldn't allocate host due to network full.
     """
     # TODO: should hostnames be unique
     # (i.e. fail if hostname already exists in this domain/service)?
@@ -261,6 +277,13 @@ def _allocate_host(hostname=None,
         ipv6_addr = _find_next_available_ip(infoblox_params,
                                             network_info[0]["_ref"])
 
+        # If couldn't find next available IPs, return error
+        if ipv4_addr == "NETWORK_FULL" or ipv6_addr == "NETWORK_FULL":
+            if ipv4_addr == "NETWORK_FULL":
+                return "IPV4_NETWORK_FULL"
+            if ipv6_addr == "NETWORK_FULL":
+                return "IPV6_NETWORK_FULL"
+
     else:
         ipv4_addr = addrs[0]
         ipv6_addr = addrs[1]
@@ -332,12 +355,17 @@ def allocate_service_host(hostname=None,
     """
     Allocate host record with both IPv4 and IPv6 address, and respective DNS
     A and AAAA records.
-    - If service_networks is provided, that one is used.
+    - If service_networks is provided, and it's in a valid container,
+    that one is used.
     - If service_networks is not provided, and host_addresses is provided,
     those specific addresses are used.
-    - If neither is not provided, new ipv4 and ipv6 networks are created and
-    those are used. Note that in this case extattrs is for the hosts and not
-    for the networks.
+    - If neither is provided:
+        - If service has configured containers, new ipv4 and ipv6 networks are
+        created and those are used. Note that in this case extattrs is for the
+        hosts and not for the networks.
+        - If service doesn't have configured containers and has configured
+        networks instead, the configured networks are used (they are filled up
+        in order of appearance in the configuration file).
     The domain name is taken from the service type and appended to the
     specified hostname.
     """
@@ -347,42 +375,81 @@ def allocate_service_host(hostname=None,
 
     assert hasattr(ipam_params, service_type) \
         and service_type != 'INFOBLOX', "Invalid service type."
-    ipv4_containers = getattr(ipam_params, service_type).V4.containers
-    ipv6_containers = getattr(ipam_params, service_type).V6.containers
+    oss_ipv4_containers = getattr(ipam_params, service_type).V4.containers
+    oss_ipv6_containers = getattr(ipam_params, service_type).V6.containers
+    oss_ipv4_networks = getattr(ipam_params, service_type).V4.networks
+    oss_ipv6_networks = getattr(ipam_params, service_type).V6.networks
     domain_name = getattr(ipam_params, service_type).domain_name
 
+    assert (oss_ipv4_containers and oss_ipv6_containers) \
+        or (oss_ipv4_networks and oss_ipv6_networks), \
+        "This service is missing either containers or networks configuration."
+    assert domain_name, "This service is missing domain_name configuration."
+
     if cname_aliases:
         cname_aliases = [alias + domain_name for alias in cname_aliases]
 
     if not service_networks and not host_addresses:
-        # IPv4
-        ipv4_network = str(allocate_service_ipv4_network(
-                       service_type=service_type).v4)
-        assert ipv4_network, \
-            "No available space for IPv4 networks for this service type."
-
-        # IPv6
-        ipv6_network = str(allocate_service_ipv6_network(
-                       service_type=service_type).v6)
-        assert ipv6_network, \
-            "No available space for IPv6 networks for this service type."
-
-        network_tuple = (ipv4_network, ipv6_network)
-        host = _allocate_host(hostname=hostname+domain_name,
-                              networks=network_tuple,
-                              cname_aliases=cname_aliases,
-                              extattrs=extattrs)
+        if oss_ipv4_containers and oss_ipv6_containers:
+        # This service has configured containers.
+        # Use them to allocate new networks that can allocate the hosts.
+
+            # IPv4
+            ipv4_network = str(allocate_service_ipv4_network(
+                           service_type=service_type).v4)
+            assert ipv4_network, \
+                "No available space for IPv4 networks for this service type."
+
+            # IPv6
+            ipv6_network = str(allocate_service_ipv6_network(
+                           service_type=service_type).v6)
+            assert ipv6_network, \
+                "No available space for IPv6 networks for this service type."
+
+        elif oss_ipv4_networks and oss_ipv6_networks:
+        # This service has configured networks.
+        # Allocate a host inside an ipv4 and ipv6 network from among them.
+            ipv4_network = str(oss_ipv4_networks[0])
+            ipv6_network = str(oss_ipv6_networks[0])
+
+        while True:
+            ipv4_network_index = 0
+            ipv6_network_index = 0
+            network_tuple = (ipv4_network, ipv6_network)
+            host = _allocate_host(hostname=hostname+domain_name,
+                                  networks=network_tuple,
+                                  cname_aliases=cname_aliases,
+                                  extattrs=extattrs)
+
+            if "NETWORK_FULL" not in host:
+                break
+            elif "IPV4" in host:
+                ipv4_network_index += 1
+                assert ipv4_network_index < len(oss_ipv4_networks), \
+                    "No available space in any IPv4 network for this service."
+                ipv4_network = str(oss_ipv4_networks[ipv4_network_index])
+            else: # IPV6 in host
+                ipv6_network_index += 1
+                assert ipv6_network_index < len(oss_ipv6_networks), \
+                    "No available space in any IPv6 network for this service."
+                ipv6_network = str(oss_ipv6_networks[ipv6_network_index])
 
     elif service_networks:
         # IPv4
         ipv4_network = service_networks.v4
-        assert any(ipv4_network.subnet_of(ipv4_container)
-                   for ipv4_container in ipv4_containers)
+        if oss_ipv4_containers:
+            assert any(ipv4_network.subnet_of(oss_ipv4_container)
+                       for oss_ipv4_container in oss_ipv4_containers)
+        else:
+            assert ipv4_network in oss_ipv4_networks
 
         # IPv6
         ipv6_network = service_networks.v6
-        assert any(ipv6_network.subnet_of(ipv6_container)
-                   for ipv6_container in ipv6_containers)
+        if oss_ipv6_containers:
+            assert any(ipv6_network.subnet_of(oss_ipv6_container)
+                       for oss_ipv6_container in oss_ipv6_containers)
+        else:
+            assert ipv6_network in oss_ipv6_networks
 
         host = _allocate_host(
             hostname=hostname+domain_name,
@@ -390,17 +457,26 @@ def allocate_service_host(hostname=None,
             cname_aliases=cname_aliases,
             extattrs=extattrs
         )
+        assert "NETWORK_FULL" not in host
 
     elif host_addresses:
         # IPv4
         ipv4_addr = host_addresses.v4
-        assert any(ipv4_addr in ipv4_container
-                   for ipv4_container in ipv4_containers)
+        if oss_ipv4_containers:
+            assert any(ipv4_addr in oss_ipv4_container
+                       for oss_ipv4_container in oss_ipv4_containers)
+        else:
+            assert any(ipv4_addr in oss_ipv4_network
+                       for oss_ipv4_network in oss_ipv4_networks)
 
         # IPv6
         ipv6_addr = host_addresses.v6
-        assert any(ipv6_addr in ipv6_container
-                   for ipv6_container in ipv6_containers)
+        if oss_ipv6_containers:
+            assert any(ipv6_addr in oss_ipv6_container
+                       for oss_ipv6_container in oss_ipv6_containers)
+        else:
+            assert any(ipv4_addr in oss_ipv6_network
+                       for oss_ipv6_network in oss_ipv6_networks)
 
         host = _allocate_host(
             hostname=hostname+domain_name,
@@ -408,6 +484,7 @@ def allocate_service_host(hostname=None,
             cname_aliases=cname_aliases,
             extattrs=extattrs
         )
+        assert "NETWORK_FULL" not in host
 
     return host
 
diff --git a/gso/services/ipam.py b/gso/services/ipam.py
index ba43780858e5e32b59d3b08a26fabba769032ee6..93a75d0d76d25ab7765bd3d9b0404e6654152246 100644
--- a/gso/services/ipam.py
+++ b/gso/services/ipam.py
@@ -57,61 +57,54 @@ def new_service_host(hostname,
         extattrs=extattrs)
 
 
-'''
+
 if __name__ == '__main__':
     # sample call flow to allocate two loopback interfaces and a trunk service
-    # new_service_host can be called passing networks or addresses
-    # - host h1 for service LO uses a specific ipv4/ipv6 address pair
-    # - the rest use the ipv4/ipv6 network pair
+    # new_service_host can be called passing networks, addresses, or nothing.
+    # - host h1 for service TRUNK uses a specific ipv4/ipv6 address pair
+    # - host h2 for service TRUNK uses the ipv4/ipv6 network pair
+    # - service LO uses nothing
     # networks and hosts can be allocated with extensible attributes
-    # - host h2 for service LO uses extattrs for both network and address/DNS
-    # - the rest don't use extattrs
+    # networks can be created with a comment
     # CNAME records can be optionally created
-    # - hosts for service TRUNK use some alias names
-
-    hostname_A = 'h1'
-    hostname_B = 'h2'
 
+    hostname_A = 'h9'
+    hostname_B = 'h10'
+    '''
     # h1 LO (loopback)
-    lo1_service_networks = new_service_networks(
-        service_type='LO',
-        comment="Network for h1 LO"
-    )
-    lo1_v4_host_address = lo1_service_networks.v4.network_address
-    lo1_v6_host_address = lo1_service_networks.v6.network_address
-    lo1_host_addresses = HostAddresses(v4=lo1_v4_host_address,
-                                       v6=lo1_v6_host_address)
     new_service_host(hostname=hostname_A+"_LO",
-                     service_type='LO',
-                     host_addresses=lo1_host_addresses)
+                     service_type='LO')
 
     # h2 LO (loopback)
-    lo2_network_extattrs = {
+    new_service_host(hostname=hostname_B+"_LO",
+                     service_type='LO')
+    '''
+    # h1-h2 TRUNK
+    trunk12_network_extattrs = {
         "vrf_name": {"value": "dummy_vrf"},
     }
-    lo2_host_extattrs = {
+    trunk12_host_extattrs = {
         "Site": {"value": "dummy_site"},
     }
-    lo2_service_networks = \
-        new_service_networks(service_type='LO',
-                             comment="Network for h2 LO",
-                             extattrs=lo2_network_extattrs)
-    new_service_host(hostname=hostname_B+"_LO",
-                     service_type='LO',
-                     service_networks=lo2_service_networks,
-                     extattrs=lo2_host_extattrs)
-
-    # h1-h2 TRUNK
     trunk12_service_networks = new_service_networks(
         service_type='TRUNK',
+        extattrs=trunk12_network_extattrs,
         comment="Network for h1-h2 TRUNK"
     )
+
+    trunk12_v4_host_address = trunk12_service_networks.v4.network_address
+    trunk12_v6_host_address = trunk12_service_networks.v6.network_address
+    trunk12_host_addresses = HostAddresses(v4=trunk12_v4_host_address,
+                                           v6=trunk12_v6_host_address)
+
     new_service_host(hostname=hostname_A+"_TRUNK",
                      service_type='TRUNK',
+                     host_addresses=trunk12_host_addresses,
                      cname_aliases=["alias1.h1", "alias2.h1"],
-                     service_networks=trunk12_service_networks)
+                     extattrs=trunk12_host_extattrs)
+
     new_service_host(hostname=hostname_B+"_TRUNK",
                      service_type='TRUNK',
+                     service_networks=trunk12_service_networks,
                      cname_aliases=["alias1.h2"],
-                     service_networks=trunk12_service_networks)
-'''
+                     extattrs=trunk12_host_extattrs)
diff --git a/gso/settings.py b/gso/settings.py
index 76e1dccfd4cce663aa61aeb50c8ebebc51eabb56..7167785cb32245f51316809db517bd09bbc251d8 100644
--- a/gso/settings.py
+++ b/gso/settings.py
@@ -18,11 +18,13 @@ class InfoBloxParams(BaseSettings):
 
 class V4NetworkParams(BaseSettings):
     containers: list[ipaddress.IPv4Network]
+    networks: list[ipaddress.IPv4Network]
     mask: int  # TODO: validation on mask?
 
 
 class V6NetworkParams(BaseSettings):
     containers: list[ipaddress.IPv6Network]
+    networks: list[ipaddress.IPv6Network]
     mask: int  # TODO: validation on mask?