diff --git a/containers/MetaDesktop/files/entrypoint.sh b/containers/MetaDesktop/files/entrypoint.sh
index f818af5cfcf39cb3b58fdd7b5dc7120e4de5490d..9fa82fa42ca0f0be9dbe4f8fc1cb840b8fc76047 100644
--- a/containers/MetaDesktop/files/entrypoint.sh
+++ b/containers/MetaDesktop/files/entrypoint.sh
@@ -7,6 +7,12 @@ set -e
 echo ""
 echo "[INFO] Executing entrypoint..."
 
+if [ "x$BASE_PORT" == "x" ]; then
+    echo "[INFO] No task base port set, will set noVNC port 8590 and VNC port 5900 with desktop id \"0\""  
+else 
+    echo "[INFO] Task base port set, will set noVNC port $BASE_PORT and noVNC port $(($BASE_PORT+1)) with desktop id \"$(($BASE_PORT-5900+1))\""
+fi
+
 #---------------------
 #   Setup home
 #---------------------
diff --git a/containers/MetaDesktop/files/run_novnc.sh b/containers/MetaDesktop/files/run_novnc.sh
index 3caeaf8dae51bbde0aef86d92b38381f64754b92..53198782b7aea92d8cd8c5181a5fefac7fa91b22 100755
--- a/containers/MetaDesktop/files/run_novnc.sh
+++ b/containers/MetaDesktop/files/run_novnc.sh
@@ -2,11 +2,11 @@
 
 # Exec TigerVNC server 
 
-if [ "x$TASK_PORT" == "x" ]; then
+if [ "x$BASE_PORT" == "x" ]; then
     /usr/lib/noVNC/utils/launch.sh --listen 8590
-    echo "Running noVN on port 8590"
+    echo "Running noVNC on port 8590"
 else
-    /usr/lib/noVNC/utils/launch.sh --listen $TASK_PORT
-    echo "Running noVN on port $TASK_PORT"
+    /usr/lib/noVNC/utils/launch.sh --listen $BASE_PORT --vnc localhost:$(($BASE_PORT+1))
+    echo "Running noVNC on port $BASE_PORT and connecting to VNC on port $(($BASE_PORT+1))"
 
 fi
diff --git a/containers/MetaDesktop/files/run_vnc.sh b/containers/MetaDesktop/files/run_vnc.sh
index d42a22269cfdff10ccc212539cde7c283460e8f6..ac7daebd51232559545e9bdcf71dbd5e79140cb6 100755
--- a/containers/MetaDesktop/files/run_vnc.sh
+++ b/containers/MetaDesktop/files/run_vnc.sh
@@ -2,10 +2,16 @@
 
 # Exec TigerVNC server 
 
+if [ "x$BASE_PORT" == "x" ]; then
+    DESKTOP_NUMBER=0
+else
+    DESKTOP_NUMBER=$(($BASE_PORT-5900+1))
+fi
+
 if [ "x$VNC_AUTH" == "xTrue" ]; then
-    /opt/tigervnc/usr/bin/vncserver :0 -SecurityTypes vncauth,tlsvnc -xstartup /opt/tigervnc/xstartup
+    /opt/tigervnc/usr/bin/vncserver :$DESKTOP_NUMBER -SecurityTypes vncauth,tlsvnc -xstartup /opt/tigervnc/xstartup
 else
-    /opt/tigervnc/usr/bin/vncserver :0 -SecurityTypes None -xstartup /opt/tigervnc/xstartup
+    /opt/tigervnc/usr/bin/vncserver :$DESKTOP_NUMBER -SecurityTypes None -xstartup /opt/tigervnc/xstartup
 fi
 
 
diff --git a/services/slurmbase/Dockerfile b/services/slurmbase/Dockerfile
index 8297d228cf4fbf96947ba29171bd7c243a75c834..7abc48159561fd7276d63db67fc088e2fe7a8956 100755
--- a/services/slurmbase/Dockerfile
+++ b/services/slurmbase/Dockerfile
@@ -22,3 +22,8 @@ COPY slurm.conf /etc/slurm-llnl/slurm.conf
 # TODO: why do we need this?
 RUN ln -s /var/lib/slurm-llnl /var/lib/slurm-wlm 
 RUN ln -s /var/log/slurm-llnl /var/log/slurm-wlm
+
+# Add slurmtestuser user
+RUN useradd slurmtestuser
+RUN cp -a /rosetta/.ssh /home/slurmtestuser
+RUN chown -R slurmtestuser:slurmtestuser /home/slurmtestuser   
diff --git a/services/webapp/code/rosetta/base_app/api.py b/services/webapp/code/rosetta/base_app/api.py
index bcc9f320a8f11f289bb1985dbd82e0521abec962..6bbcb2c33307da05860b8a06d4152f60a1cef317 100644
--- a/services/webapp/code/rosetta/base_app/api.py
+++ b/services/webapp/code/rosetta/base_app/api.py
@@ -260,18 +260,20 @@ from random import randint
 while True:
 
     # Get a random ephimeral port
-    port = randint(49152, 65535)
+    port = randint(49152, 65535-2)
 
     # Check port is available
     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    result = sock.connect_ex(('127.0.0.1', port))
-    if result == 0:
-        print('Found not available ephimeral port ({}) , choosing another one...'.format(port))
+    result1 = sock.connect_ex(('127.0.0.1', port))
+    result2 = sock.connect_ex(('127.0.0.1', port+1))
+    result3 = sock.connect_ex(('127.0.0.1', port+2))
+    if (result1 == 0) or (result2 == 0) or (result3 == 0):
+        logger.info('Found not available ephemeral port triplet ({},{},{}) , choosing another one...'.format(port,port+1,port+2))
         import time
         time.sleep(1)
     else:
         break
-logger.info(' - port: "{}"'.format(port))
+logger.info(' - ports: "{},{},{}"'.format(port, port+1, port+2))
 
 response = urlopen("'''+host_conn_string+'''/api/v1/base/agent/?task_uuid={}&action=set_ip_port&ip={}&port={}".format(task_uuid, ip, port))
 response_content = response.read() 
diff --git a/services/webapp/code/rosetta/base_app/computing_managers.py b/services/webapp/code/rosetta/base_app/computing_managers.py
index dc797c2ac48286846e6eec956d59854d52136caf..3ad3aaa6d5a957b20560b4cc878e1c719700976f 100644
--- a/services/webapp/code/rosetta/base_app/computing_managers.py
+++ b/services/webapp/code/rosetta/base_app/computing_managers.py
@@ -116,7 +116,7 @@ class LocalComputingManager(ComputingManager):
             task.tid    = task_tid
             task.status = TaskStatuses.running
             task.ip     = task_ip
-            task.port   = int(task.container.service_ports.split(',')[0])
+            task.port   = int(task.container.default_ports.split(',')[0])
 
             # Save
             task.save()
@@ -179,13 +179,13 @@ class RemoteComputingManager(ComputingManager):
             webapp_ip = socket.gethostbyname(hostname)
 
             run_command  = 'ssh -i {} -4 -o StrictHostKeyChecking=no {} '.format(user_keys.private_key_file, host)
-            run_command+= '"wget {}:8080/api/v1/base/agent/?task_uuid={} -O /tmp/agent_{}.py &> /dev/null && export TASK_PORT=\$(python /tmp/agent_{}.py 2> /tmp/{}.log) && '.format(webapp_ip, task.uuid, task.uuid, task.uuid, task.uuid)
-            run_command += 'export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_TASK_PORT=\$TASK_PORT && {} '.format(authstring)
+            run_command+= '"wget {}:8080/api/v1/base/agent/?task_uuid={} -O /tmp/agent_{}.py &> /dev/null && export BASE_PORT=\$(python /tmp/agent_{}.py 2> /tmp/{}.log) && '.format(webapp_ip, task.uuid, task.uuid, task.uuid, task.uuid)
+            run_command += 'export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_BASE_PORT=\$BASE_PORT && {} '.format(authstring)
             run_command += 'exec nohup singularity run --pid --writable-tmpfs --containall --cleanenv '
             
             # ssh -i /rosetta/.ssh/id_rsa -4 -o StrictHostKeyChecking=no slurmclusterworker-one
             # "wget 172.21.0.2:8080/api/v1/base/agent/?task_uuid=15a4320a-88b6-4ffc-8dd0-c80f9d18b292 -O /tmp/agent_15a4320a-88b6-4ffc-8dd0-c80f9d18b292.py &> /dev/null &&
-            # export TASK_PORT=\$(python /tmp/agent_15a4320a-88b6-4ffc-8dd0-c80f9d18b292.py) && export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_TASK_PORT=\$TASK_PORT &&  export SINGULARITYENV_AUTH_PASS=testpass &&  
+            # export BASE_PORT=\$(python /tmp/agent_15a4320a-88b6-4ffc-8dd0-c80f9d18b292.py) && export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_BASE_PORT=\$BASE_PORT &&  export SINGULARITYENV_AUTH_PASS=testpass &&  
             # exec nohup singularity run --pid --writable-tmpfs --containall --cleanenv
             # docker://dregistry:5000/rosetta/metadesktop &> /tmp/15a4320a-88b6-4ffc-8dd0-c80f9d18b292.log & echo $!"
             
@@ -286,6 +286,9 @@ class SlurmComputingManager(ComputingManager):
  
         if task.container.type == 'singularity':
 
+            if not task.dynamic_ports:
+                raise Exception('This task does not support dynamic port allocation and is therefore not supported using singularity on Slurm')
+
             # Set pass if any
             if task.auth_pass:
                 authstring = ' export SINGULARITYENV_AUTH_PASS={} && '.format(task.auth_pass)
@@ -298,15 +301,15 @@ class SlurmComputingManager(ComputingManager):
 
             run_command = 'ssh -i {} -4 -o StrictHostKeyChecking=no {} '.format(user_keys.private_key_file, host)
 
-            run_command += '"echo \\"#!/bin/bash\nwget {}:8080/api/v1/base/agent/?task_uuid={} -O /tmp/agent_{}.py &> /dev/null && export TASK_PORT=\\\\\\$(python /tmp/agent_{}.py 2> /tmp/{}.log) && '.format(webapp_ip, task.uuid, task.uuid, task.uuid, task.uuid)
-            run_command += 'export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_TASK_PORT=\\\\\\$TASK_PORT && {} '.format(authstring)
+            run_command += '"echo \\"#!/bin/bash\nwget {}:8080/api/v1/base/agent/?task_uuid={} -O /tmp/agent_{}.py &> /dev/null && export BASE_PORT=\\\\\\$(python /tmp/agent_{}.py 2> /tmp/{}.log) && '.format(webapp_ip, task.uuid, task.uuid, task.uuid, task.uuid)
+            run_command += 'export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_BASE_PORT=\\\\\\$BASE_PORT && {} '.format(authstring)
             run_command += 'exec nohup singularity run --pid --writable-tmpfs --containall --cleanenv '
 
 
             # Double to escape for python six for shell (double times three as \\\ escapes a single slash in shell)
 
             # ssh -i /rosetta/.ssh/id_rsa -4 -o StrictHostKeyChecking=no slurmclustermaster-main "echo \"wget 172.18.0.5:8080/api/v1/base/agent/?task_uuid=558c65c3-8b72-4d6b-8119-e1dcf6f81177 -O /tmp/agent_558c65c3-8b72-4d6b-8119-e1dcf6f81177.py &> /dev/null
-            #  && export TASK_PORT=\\\$(python /tmp/agent_558c65c3-8b72-4d6b-8119-e1dcf6f81177.py 2> /tmp/558c65c3-8b72-4d6b-8119-e1dcf6f81177.log) && export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_TASK_PORT=\\\$TASK_PORT &&  export SINGULARITYENV_AUTH_PASS=testpass 
+            #  && export BASE_PORT=\\\$(python /tmp/agent_558c65c3-8b72-4d6b-8119-e1dcf6f81177.py 2> /tmp/558c65c3-8b72-4d6b-8119-e1dcf6f81177.log) && export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_BASE_PORT=\\\$BASE_PORT &&  export SINGULARITYENV_AUTH_PASS=testpass 
             #  && exec nohup singularity run --pid --writable-tmpfs --containall --cleanenv docker://dregistry:5000/rosetta/metadesktop &> /tmp/558c65c3-8b72-4d6b-8119-e1dcf6f81177.log\" > /tmp/558c65c3-8b72-4d6b-8119-e1dcf6f81177.sh"
 
             
diff --git a/services/webapp/code/rosetta/base_app/management/commands/base_app_populate.py b/services/webapp/code/rosetta/base_app/management/commands/base_app_populate.py
index bad2e58bb28083927e812c4e125884276f368e36..28ce4ac86c99840d5b16439d3118ec2dcf85c3da 100644
--- a/services/webapp/code/rosetta/base_app/management/commands/base_app_populate.py
+++ b/services/webapp/code/rosetta/base_app/management/commands/base_app_populate.py
@@ -53,7 +53,8 @@ class Command(BaseCommand):
                                      image         = 'rosetta/metadesktop',
                                      type          = 'docker',
                                      registry      = 'docker_local',
-                                     service_ports = '8590',
+                                     default_ports = '8590',
+                                     dynamic_ports = True,
                                      require_pass  = True)
 
             # MetaDesktop Singularity
@@ -62,7 +63,8 @@ class Command(BaseCommand):
                                      image         = 'rosetta/metadesktop',
                                      type          = 'singularity',
                                      registry      = 'docker_local',
-                                     service_ports = '8590',
+                                     default_ports = '8590',
+                                     dynamic_ports = True,
                                      require_pass  = True)
 
             # Astrocook
@@ -71,15 +73,16 @@ class Command(BaseCommand):
                                      image         = 'sarusso/astrocook:b2b819e',
                                      type          = 'docker',
                                      registry      = 'docker_local',
-                                     service_ports = '8590')
+                                     dynamic_ports = False,
+                                     default_ports = '8590')
 
 
-        # Public containers
+        # Private containers
         testuser_containers = Container.objects.filter(user=testuser)
         if testuser_containers:
-            print('Not creating testuser containers as they already exist')
+            print('Not creating testuser private containers as they already exist')
         else:
-            print('Creating testuser containers...')
+            print('Creating testuser private containers...')
             
             # JuPyter
             Container.objects.create(user          = testuser,
@@ -87,7 +90,8 @@ class Command(BaseCommand):
                                      image         = 'jupyter/base-notebook',
                                      type          = 'docker',
                                      registry      = 'docker_hub',
-                                     service_ports = '8888')
+                                     dynamic_ports = False,
+                                     default_ports = '8888')
 
         # Computing resources
         computing_resources = Computing.objects.all()
@@ -122,7 +126,7 @@ class Command(BaseCommand):
 
             ComputingUserConf.objects.create(user      = testuser,
                                              computing = demo_remote_auth_computing,
-                                             data      = {'user': 'testuser'})
+                                             data      = {'user': 'slurmtestuser'})
          
 
             #==============================
@@ -142,6 +146,6 @@ class Command(BaseCommand):
             # Create demo slurm user computing conf
             ComputingUserConf.objects.create(user      = testuser,
                                              computing = demo_slurm_computing,
-                                             data      = {'user': 'testuser'})
+                                             data      = {'user': 'slurmtestuser'})
 
 
diff --git a/services/webapp/code/rosetta/base_app/models.py b/services/webapp/code/rosetta/base_app/models.py
index 57c0c5d033774507607ffb81fbc12a46053263fc..2110a1a6eb7f0559a73cf37f4cec26b1249fa4bc 100644
--- a/services/webapp/code/rosetta/base_app/models.py
+++ b/services/webapp/code/rosetta/base_app/models.py
@@ -80,14 +80,14 @@ class Container(models.Model):
     image         = models.CharField('Container image', max_length=255, blank=False, null=False)
     type          = models.CharField('Container type', max_length=36, blank=False, null=False)
     registry      = models.CharField('Container registry', max_length=255, blank=False, null=False)
-    service_ports = models.CharField('Container service ports', max_length=36, blank=True, null=True)
-    #private       = models.BooleanField('Container is private and needs auth to be pulled from the registry')
+    default_ports = models.CharField('Container service ports', max_length=36, blank=True, null=True)
+    dynamic_ports = models.BooleanField(default=False)
     require_user  = models.BooleanField(default=False)
     require_pass  = models.BooleanField(default=False)
 
 
     def __str__(self):
-        return str('Container of type "{}" with image "{}" with service ports "{}" from registry "{}" of user "{}"'.format(self.type, self.image, self.service_ports, self.registry, self.user))
+        return str('Container of type "{}" with image "{}" with service ports "{}" from registry "{}" of user "{}"'.format(self.type, self.image, self.default_ports, self.registry, self.user))
 
 
     @property
diff --git a/services/webapp/code/rosetta/base_app/templates/components/container.html b/services/webapp/code/rosetta/base_app/templates/components/container.html
index e4c1edb77da7084e7a1b213c2193b8f5230b8d57..41486eed627c76fba07019bcc74876954f6827af 100644
--- a/services/webapp/code/rosetta/base_app/templates/components/container.html
+++ b/services/webapp/code/rosetta/base_app/templates/components/container.html
@@ -32,7 +32,7 @@
 
        <tr>
         <td><b>Service port(s)</b></td>
-        <td>{{ container.service_ports}}</td>
+        <td>{{ container.default_ports}}</td>
        </tr>
        
        {% if container.user %}
diff --git a/services/webapp/code/rosetta/base_app/views.py b/services/webapp/code/rosetta/base_app/views.py
index 341a45d82b92ac91285acb0c562f3d7ae0eeea89..95cb1578dcc4ac3d350941f5840408d6d110ab89 100644
--- a/services/webapp/code/rosetta/base_app/views.py
+++ b/services/webapp/code/rosetta/base_app/views.py
@@ -621,17 +621,17 @@ def add_container(request):
         container_name = request.POST.get('container_name', None)
 
         # Container service ports. TODO: support multiple ports? 
-        container_service_ports = request.POST.get('container_service_ports', None)
+        container_default_ports = request.POST.get('container_default_ports', None)
         
-        if container_service_ports:       
+        if container_default_ports:       
             try:
-                for container_service_port in container_service_ports.split(','):
+                for container_service_port in container_default_ports.split(','):
                     int(container_service_port)
             except:
-                raise ErrorMessage('Invalid service port(s) in "{}"'.format(container_service_ports))
+                raise ErrorMessage('Invalid service port(s) in "{}"'.format(container_default_ports))
 
         # Log
-        logger.debug('Creating new container object with image="{}", type="{}", registry="{}", service_ports="{}"'.format(container_image, container_type, container_registry, container_service_ports))
+        logger.debug('Creating new container object with image="{}", type="{}", registry="{}", default_ports="{}"'.format(container_image, container_type, container_registry, container_default_ports))
 
         # Create
         Container.objects.create(user          = request.user,
@@ -639,7 +639,7 @@ def add_container(request):
                                  name          = container_name,
                                  type          = container_type,
                                  registry      = container_registry,
-                                 service_ports = container_service_ports)
+                                 default_ports = container_default_ports)
         # Set added switch
         data['added'] = True