diff --git a/app/models.py b/app/models.py
index 8ad58ba67d2ff19a739da5c53d900f4b5eef0b7e..176ddbad1fa2cc676f89567df8d420cf47f28936 100644
--- a/app/models.py
+++ b/app/models.py
@@ -272,6 +272,32 @@ class User(db.Model, UserMixin):
             return False
         return str(network.domain) in self.csentry_domains
 
+    def can_create_vm(self, host):
+        """Return True if the user can create the VM
+
+        - host.device_type shall be VirtualMachine
+        - admin users can create anything
+        - normal users must have access to the network to create VIOC
+        - normal users can only create a VM if the host is in one of the allowed domains
+        - LOGIN_DISABLED can be set to True to turn off authentication check when testing.
+          In this case, this function always returns True.
+        """
+        if str(host.device_type) != "VirtualMachine":
+            return False
+        if current_app.config.get("LOGIN_DISABLED") or self.is_admin:
+            return True
+        if not self.has_access_to_network(host.main_network):
+            # True is already returned for admin users
+            return False
+        if host.is_ioc:
+            # VIOC can be created by anyone having access to the network
+            return True
+        # VM can only be created if the domain is allowed
+        return (
+            str(host.main_interface.network.domain)
+            in current_app.config["ALLOWED_VM_CREATION_DOMAINS"]
+        )
+
     def favorite_attributes(self):
         """Return all user's favorite attributes"""
         favorites_list = [
diff --git a/app/network/views.py b/app/network/views.py
index 88aaffe3adb94e103cf427e596948d79e7e4ce57..bfe70f2ad83e83734552ceba635b8d7e7a3bef99 100644
--- a/app/network/views.py
+++ b/app/network/views.py
@@ -178,8 +178,11 @@ def view_host(name):
                 )
                 return redirect(url_for("task.view_task", id_=task.id))
         else:
-            if not current_user.is_admin:
-                flash(f"Only admin users are allowed to create a VM!", "info")
+            if not current_user.can_create_vm(host):
+                flash(
+                    f"You don't have the proper permissions to create this VM. Please contact an admin user.",
+                    "warning",
+                )
                 return redirect(url_for("network.view_host", name=name))
             else:
                 task = tasks.trigger_vm_creation(
diff --git a/app/settings.py b/app/settings.py
index 094c8dd579762cd63b4bc2c11c9321407477a759..63e824f494039c828771db4531415adb94c9dae1 100644
--- a/app/settings.py
+++ b/app/settings.py
@@ -73,6 +73,8 @@ CSENTRY_DOMAINS_LDAP_GROUPS = {
     "tn.esss.lu.se": ["ICS Employees", "ICS Consultants"],
     "cslab.esss.lu.se": ["ICS Employees", "ICS Consultants"],
 }
+# List of domains where users can create a VM
+ALLOWED_VM_CREATION_DOMAINS = ["cslab.esss.lu.se"]
 
 NETWORK_DEFAULT_PREFIX = 24
 # ICS Ids starting with this prefix are considered temporary and can be changed
diff --git a/app/templates/network/view_host.html b/app/templates/network/view_host.html
index 10a2208cd5646f0bf5fa4b54f441a2c3dea0e254..240ba550f6b066a55168a8eb9de2d6040269ae34 100644
--- a/app/templates/network/view_host.html
+++ b/app/templates/network/view_host.html
@@ -66,7 +66,7 @@
         {% endif %}
       </dl>
     </div>
-    {% if host.device_type.name.startswith('Virtual') and current_user.is_admin %}
+    {% if host.device_type.name.startswith('Virtual') %}
       {% if host.is_ioc %}
         {% set vm_type = 'Virtual IOC' %}
       {% else %}
diff --git a/docs/network.rst b/docs/network.rst
index 656bd9f0af9bbe6a9516d5fb47a2bb59666eeb74..8fde5890b5c4f7c125d2c5ef1cbba2c13f2f9c08 100644
--- a/docs/network.rst
+++ b/docs/network.rst
@@ -123,8 +123,6 @@ See :ref:`task`.
 VM creation
 -----------
 
-This is currently restricted to admin users!
-
 From the *View host* page, you can trigger the creation of a Virtual machine:
 
 .. image:: _static/create_vm.png
@@ -144,6 +142,10 @@ The post install job name can be defined per domain in the **AWX_POST_INSTALL**
 Admin users can disable the post install job run by selecting the *Skip post install job* checkbox.
 This checkbox is only visible to admin users.
 
+A VIOC can be created by any user who has access to the associated network/domain.
+The same restriction applies to VMs but the associated domain must also be part of the **ALLOWED_VM_CREATION_DOMAINS** list (currently cslab.tn.esss.lu.se).
+Please contact an admin user if you don't have the proper permissions.
+
 Ansible inventory
 -----------------
 
diff --git a/tests/functional/conftest.py b/tests/functional/conftest.py
index a653fc2a844526944a454829c5426089955a82d5..7d7cc0006b3e49e0fcf7952b8cb68bee4b8150b0 100644
--- a/tests/functional/conftest.py
+++ b/tests/functional/conftest.py
@@ -59,6 +59,7 @@ def app(request):
             "foo.example.org": ["CSEntry User", "CSEntry Consultant"],
         },
         "AWX_URL": "https://awx.example.org",
+        "ALLOWED_VM_CREATION_DOMAINS": ["lab.example.org"],
     }
     app = create_app(config=config)
     ctx = app.app_context()
diff --git a/tests/functional/test_models.py b/tests/functional/test_models.py
index ecfd31791fb2d11a8cf17dbe53c033a9e6176616..18c491c9188e903fe291f4de1e035c920f0be87e 100644
--- a/tests/functional/test_models.py
+++ b/tests/functional/test_models.py
@@ -77,6 +77,83 @@ def test_user_has_access_to_network(user_factory, domain_factory, network_factor
     assert user.has_access_to_network(None)
 
 
+def test_user_can_create_vm(
+    user_factory,
+    domain_factory,
+    network_factory,
+    device_type_factory,
+    host_factory,
+    interface_factory,
+):
+    virtualmachine = device_type_factory(name="VirtualMachine")
+    domain_prod = domain_factory(name="prod.example.org")
+    domain_lab = domain_factory(name="lab.example.org")
+    network_prod = network_factory(domain=domain_prod)
+    network_lab = network_factory(domain=domain_lab)
+    network_lab_admin = network_factory(domain=domain_lab, admin_only=True)
+    vm_prod = host_factory(device_type=virtualmachine)
+    interface_factory(name=vm_prod.name, host=vm_prod, network=network_prod)
+    vioc_prod = host_factory(device_type=virtualmachine, is_ioc=True)
+    interface_factory(name=vioc_prod.name, host=vioc_prod, network=network_prod)
+    vm_lab = host_factory(device_type=virtualmachine)
+    interface_factory(name=vm_lab.name, host=vm_lab, network=network_lab)
+    vioc_lab = host_factory(device_type=virtualmachine, is_ioc=True)
+    interface_factory(name=vioc_lab.name, host=vioc_lab, network=network_lab)
+    vm_lab_admin = host_factory(device_type=virtualmachine)
+    interface_factory(
+        name=vm_lab_admin.name, host=vm_lab_admin, network=network_lab_admin
+    )
+    vioc_lab_admin = host_factory(device_type=virtualmachine, is_ioc=True)
+    interface_factory(
+        name=vioc_lab_admin.name, host=vioc_lab_admin, network=network_lab_admin
+    )
+    non_vm = host_factory()
+    non_vm_ioc = host_factory(is_ioc=True)
+    interface_factory(name=non_vm.name, host=non_vm, network=network_lab)
+    interface_factory(name=non_vm_ioc.name, host=non_vm_ioc, network=network_lab)
+    # User has access to prod and lab networks but can only create a VM in the lab
+    # (due to ALLOWED_VM_CREATION_DOMAINS) and VIOC in both
+    user = user_factory(groups=["CSEntry Prod", "CSEntry Lab"])
+    assert user.can_create_vm(vm_lab)
+    assert not user.can_create_vm(vm_prod)
+    assert not user.can_create_vm(vm_lab_admin)
+    assert not user.can_create_vm(non_vm)
+    assert user.can_create_vm(vioc_lab)
+    assert user.can_create_vm(vioc_prod)
+    assert not user.can_create_vm(vioc_lab_admin)
+    assert not user.can_create_vm(non_vm_ioc)
+    # User has only access to the lab networks and can only create a VM and VIOC in the lab
+    user = user_factory(groups=["foo", "CSEntry Lab"])
+    assert user.can_create_vm(vm_lab)
+    assert not user.can_create_vm(vm_prod)
+    assert not user.can_create_vm(vm_lab_admin)
+    assert not user.can_create_vm(non_vm)
+    assert user.can_create_vm(vioc_lab)
+    assert not user.can_create_vm(vioc_prod)
+    assert not user.can_create_vm(vioc_lab_admin)
+    assert not user.can_create_vm(non_vm_ioc)
+    # User can't create any VM or VIOC
+    user = user_factory(groups=["one", "two"])
+    assert not user.can_create_vm(vm_lab)
+    assert not user.can_create_vm(vm_prod)
+    assert not user.can_create_vm(vm_lab_admin)
+    assert not user.can_create_vm(non_vm)
+    assert not user.can_create_vm(vioc_lab)
+    assert not user.can_create_vm(vioc_prod)
+    assert not user.can_create_vm(vioc_lab_admin)
+    assert not user.can_create_vm(non_vm_ioc)
+    # Admin can create VM and VIOC
+    user = user_factory(groups=["CSEntry Admin"])
+    assert user.can_create_vm(vm_lab)
+    assert user.can_create_vm(vm_prod)
+    assert user.can_create_vm(vm_lab_admin)
+    assert not user.can_create_vm(non_vm)
+    assert user.can_create_vm(vioc_lab)
+    assert user.can_create_vm(vioc_prod)
+    assert user.can_create_vm(vioc_lab_admin)
+    assert not user.can_create_vm(non_vm_ioc)
+
+
 def test_network_ip_properties(network_factory):
     # Create some networks
     network1 = network_factory(
diff --git a/tests/functional/test_web.py b/tests/functional/test_web.py
index f37dfe4305957cb184f1f1d85cf6107ef5d701bf..a5030fc89badec875302d15a8077ae70edae04f9 100644
--- a/tests/functional/test_web.py
+++ b/tests/functional/test_web.py
@@ -429,3 +429,71 @@ def test_create_interface(
     assert interface.mac == mac
     assert interface.name == name
     assert interface.host == host
+
+
+def check_vm_creation_response(response, success=True):
+    assert response.status_code == 200
+    assert (b"View task" in response.data) is success
+    assert (b"View host" in response.data) is not success
+    assert (b"Please contact an admin user" in response.data) is not success
+
+
+def test_create_vm(
+    client,
+    domain_factory,
+    network_factory,
+    device_type_factory,
+    host_factory,
+    interface_factory,
+):
+    virtualmachine = device_type_factory(name="VirtualMachine")
+    domain_prod = domain_factory(name="prod.example.org")
+    domain_lab = domain_factory(name="lab.example.org")
+    network_prod = network_factory(domain=domain_prod)
+    network_lab = network_factory(domain=domain_lab)
+    vm_prod = host_factory(device_type=virtualmachine)
+    interface_factory(name=vm_prod.name, host=vm_prod, network=network_prod)
+    vioc_prod = host_factory(device_type=virtualmachine, is_ioc=True)
+    interface_factory(name=vioc_prod.name, host=vioc_prod, network=network_prod)
+    vm_lab = host_factory(device_type=virtualmachine)
+    interface_factory(name=vm_lab.name, host=vm_lab, network=network_lab)
+    vioc_lab = host_factory(device_type=virtualmachine, is_ioc=True)
+    interface_factory(name=vioc_lab.name, host=vioc_lab, network=network_lab)
+    form = {"cores": 1, "memory": 4, "disk": 15, "osversion": "centos7"}
+    # User has access to the lab networks and can create VM and VIOC there
+    login(client, "user_lab", "userlab")
+    response = client.post(
+        f"/network/hosts/view/{vm_prod.name}", data=form, follow_redirects=True
+    )
+    check_vm_creation_response(response, success=False)
+    response = client.post(
+        f"/network/hosts/view/{vioc_prod.name}", data=form, follow_redirects=True
+    )
+    check_vm_creation_response(response, success=False)
+    response = client.post(
+        f"/network/hosts/view/{vm_lab.name}", data=form, follow_redirects=True
+    )
+    check_vm_creation_response(response, success=True)
+    response = client.post(
+        f"/network/hosts/view/{vioc_lab.name}", data=form, follow_redirects=True
+    )
+    check_vm_creation_response(response, success=True)
+    logout(client)
+    # User has access to the prod networks but can only create VIOC due to ALLOWED_VM_CREATION_DOMAINS
+    login(client, "user_prod", "userprod")
+    response = client.post(
+        f"/network/hosts/view/{vm_prod.name}", data=form, follow_redirects=True
+    )
+    check_vm_creation_response(response, success=False)
+    response = client.post(
+        f"/network/hosts/view/{vioc_prod.name}", data=form, follow_redirects=True
+    )
+    check_vm_creation_response(response, success=True)
+    response = client.post(
+        f"/network/hosts/view/{vm_lab.name}", data=form, follow_redirects=True
+    )
+    check_vm_creation_response(response, success=False)
+    response = client.post(
+        f"/network/hosts/view/{vioc_lab.name}", data=form, follow_redirects=True
+    )
+    check_vm_creation_response(response, success=False)