diff --git a/.env b/.env
index c255a0ca4e..f21fc6c4d2 100644
--- a/.env
+++ b/.env
@@ -15,4 +15,3 @@ SOURCE_REPOSITORY_REF=master
 DOC_LINK ='https://opendatahub.io/docs.html'
 COMMUNITY_LINK ='https://opendatahub.io/community.html'
 ENABLED_APPS_CM = 'odh-enabled-applications-config'
-ADMIN_GROUP = 'odh-admins'
diff --git a/Makefile b/Makefile
index 6d502b7984..e3abc404f2 100644
--- a/Makefile
+++ b/Makefile
@@ -18,22 +18,6 @@ reinstall: build push undeploy deploy
 
 ##################################
 
-# DEV - run apps locally for development
-
-.PHONY: dev-frontend
-dev-frontend:
-	./install/dev-frontend.sh
-
-.PHONY: dev-backend
-dev-backend:
-	./install/dev-backend.sh
-
-.PHONY: dev
-dev:
-	./install/dev.sh
-
-##################################
-
 # BUILD - build image locally using s2i
 
 .PHONY: build
diff --git a/README.md b/README.md
index 9545ef0e41..3b0cfb47d8 100644
--- a/README.md
+++ b/README.md
@@ -27,8 +27,14 @@ Before developing for ODH, the basic requirements:
       $ cd odh-dashboard && npm install
       ```
 
+
+### Build project
+  ```
+  $ npm run build
+  ```
+  
 ### Serve development content
-This is the default context for running a local UI
+This is the default context for running a local UI.  Make sure you build the project using the instructions above prior to running the command below.
 
   ```
   $ npm run start
diff --git a/backend/src/routes/api/cluster-settings/clusterSettingsUtils.ts b/backend/src/routes/api/cluster-settings/clusterSettingsUtils.ts
index d5165d0425..bd3433bd75 100644
--- a/backend/src/routes/api/cluster-settings/clusterSettingsUtils.ts
+++ b/backend/src/routes/api/cluster-settings/clusterSettingsUtils.ts
@@ -13,12 +13,12 @@ export const updateClusterSettings = async (
   const query = request.query as { [key: string]: string };
   try {
     const jupyterhubCM = await coreV1Api.readNamespacedConfigMap(name, namespace);
-    if (query.pvcSize) {
+    if (query.pvcSize && query.cullerTimeout) {
       await coreV1Api.patchNamespacedConfigMap(
         name,
         namespace,
         {
-          data: { singleuser_pvc_size: `${query.pvcSize}Gi` },
+          data: { singleuser_pvc_size: `${query.pvcSize}Gi`, culler_timeout: query.cullerTimeout },
         },
         undefined,
         undefined,
@@ -33,6 +33,11 @@ export const updateClusterSettings = async (
       if (jupyterhubCM.body.data.singleuser_pvc_size.replace('Gi', '') !== query.pvcSize) {
         await scaleDeploymentConfig(fastify, 'jupyterhub', 0);
       }
+      if (jupyterhubCM.body.data['culler_timeout'] !== query.cullerTimeout) {
+        // scale down to 0 and scale it up to 1
+        await scaleDeploymentConfig(fastify, 'jupyterhub-idle-culler', 0);
+        await scaleDeploymentConfig(fastify, 'jupyterhub-idle-culler', 1);
+      }
     }
     return { success: true, error: null };
   } catch (e) {
@@ -52,6 +57,7 @@ export const getClusterSettings = async (
     const clusterSettingsRes = await coreV1Api.readNamespacedConfigMap(name, namespace);
     return {
       pvcSize: Number(clusterSettingsRes.body.data.singleuser_pvc_size.replace('Gi', '')),
+      cullerTimeout: Number(clusterSettingsRes.body.data.culler_timeout),
     };
   } catch (e) {
     if (e.response?.statusCode !== 404) {
diff --git a/backend/src/routes/api/cluster-settings/index.ts b/backend/src/routes/api/cluster-settings/index.ts
index 08c0bf8650..4b00eb8bbf 100644
--- a/backend/src/routes/api/cluster-settings/index.ts
+++ b/backend/src/routes/api/cluster-settings/index.ts
@@ -1,5 +1,4 @@
 import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
-import { DEV_MODE } from '../../../utils/constants';
 import { getClusterSettings, updateClusterSettings } from './clusterSettingsUtils';
 
 export default async (fastify: FastifyInstance): Promise<void> => {
diff --git a/backend/src/routes/api/status/index.ts b/backend/src/routes/api/status/index.ts
index c8ae9d31e6..6f317bc934 100644
--- a/backend/src/routes/api/status/index.ts
+++ b/backend/src/routes/api/status/index.ts
@@ -10,7 +10,6 @@ const status = async (
   fastify: KubeFastifyInstance,
   request: FastifyRequest,
 ): Promise<{ kube: KubeStatus }> => {
-  const adminGroup = process.env.ADMIN_GROUP;
   const kubeContext = fastify.kube.currentContext;
   const { currentContext, namespace, currentUser, clusterID } = fastify.kube;
   const currentUserName =
@@ -20,10 +19,14 @@ const status = async (
     userName = 'kube:admin';
   }
   const customObjectsApi = fastify.kube.customObjectsApi;
+  const coreV1Api = fastify.kube.coreV1Api;
   let isAdmin = false;
 
   try {
-    //const adminGroup2 = (await coreV1Api.readNamespacedConfigMap('rhods-groups-config', namespace)).body.data['admin_groups'];
+    const configGroupName = (await coreV1Api.readNamespacedConfigMap('groups-config', namespace))
+      .body.data['groups-config'];
+    const adminGroup = (await coreV1Api.readNamespacedConfigMap(configGroupName, namespace)).body
+      .data['admin_groups'];
     const adminGroupResponse = await customObjectsApi.getClusterCustomObject(
       'user.openshift.io',
       'v1',
@@ -33,7 +36,7 @@ const status = async (
     const adminUsers = (adminGroupResponse.body as groupObjResponse).users;
     isAdmin = adminUsers.includes(userName);
   } catch (e) {
-    console.log('Failed to get role bindings: ' + e.toString());
+    console.log('Failed to get groups: ' + e.toString());
   }
   fastify.kube.coreV1Api.getAPIResources();
   if (!kubeContext && !kubeContext.trim()) {
diff --git a/backend/src/types.ts b/backend/src/types.ts
index ef1b6a6548..cb89a114a8 100644
--- a/backend/src/types.ts
+++ b/backend/src/types.ts
@@ -10,6 +10,7 @@ export type DashboardConfig = {
 
 export type ClusterSettings = {
   pvcSize: number;
+  cullerTimeout: number;
 }
 
 // Add a minimal QuickStart type here as there is no way to get types without pulling in frontend (React) modules
diff --git a/frontend/config/dotenv.js b/frontend/config/dotenv.js
index 4bd89a0bc8..a19cb6d8d8 100644
--- a/frontend/config/dotenv.js
+++ b/frontend/config/dotenv.js
@@ -144,6 +144,7 @@ const setupDotenvFilesForEnv = ({ env }) => {
   const DIST_DIR = path.resolve(RELATIVE_DIRNAME, process.env.ODH_DIST_DIR || TS_OUT_DIR || 'public');
   const HOST = process.env.ODH_HOST || 'localhost';
   const PORT = process.env.ODH_PORT || '3000';
+  const BACKEND_PORT = process.env.PORT || process.env.BACKEND_PORT || 8080;
   const DEV_MODE = process.env.ODH_DEV_MODE || undefined;
   const OUTPUT_ONLY = process.env._ODH_OUTPUT_ONLY === 'true';
 
@@ -158,6 +159,7 @@ const setupDotenvFilesForEnv = ({ env }) => {
   process.env._ODH_PORT = PORT;
   process.env._ODH_OUTPUT_ONLY = OUTPUT_ONLY;
   process.env._ODH_DEV_MODE = DEV_MODE;
+  process.env._BACKEND_PORT = BACKEND_PORT;
 };
 
 module.exports = { setupWebpackDotenvFilesForEnv, setupDotenvFilesForEnv };
diff --git a/frontend/config/webpack.dev.js b/frontend/config/webpack.dev.js
index 23036e5be0..47fe7c8dbc 100644
--- a/frontend/config/webpack.dev.js
+++ b/frontend/config/webpack.dev.js
@@ -12,7 +12,7 @@ const COMMON_DIR = process.env._ODH_COMMON_DIR;
 const DIST_DIR = process.env._ODH_DIST_DIR;
 const HOST = process.env._ODH_HOST;
 const PORT = process.env._ODH_PORT;
-const BACKEND_PORT = process.env.BACKEND_PORT || 8080;
+const BACKEND_PORT = process.env._BACKEND_PORT;
 
 module.exports = merge(
   {
diff --git a/frontend/src/pages/clusterSettings/ClusterSettings.scss b/frontend/src/pages/clusterSettings/ClusterSettings.scss
index 9e17638843..dc5a422de2 100644
--- a/frontend/src/pages/clusterSettings/ClusterSettings.scss
+++ b/frontend/src/pages/clusterSettings/ClusterSettings.scss
@@ -1,20 +1,48 @@
 .odh-cluster-settings {
-    .pf-c-form__group {
-      border: var(--pf-global--BorderWidth--sm) solid var(--pf-global--BorderColor--100);
-      padding: var(--pf-global--spacer--md);
-      margin: 0;
-    }
+  .pf-c-form__group {
+    border: var(--pf-global--BorderWidth--sm) solid var(--pf-global--BorderColor--100);
+    padding: var(--pf-global--spacer--md);
+    margin: 0;
+  }
 
-    .odh-number-input { 
-      max-width: 100px;
+  .odh-number-input { 
+    max-width: 100px;
+  }
+
+  .pf-c-input-group {
+    padding-top: var(--pf-global--spacer--md);
+    padding-bottom: var(--pf-global--spacer--md);
+    &__text {
+      font-size: var(--pf-global--FontSize--sm);
+      color: var(--pf-global--Color--100);
     }
+  }
+
+  .pf-c-helper-text {
+    margin-top: var(--pf-global--spacer--sm);
+  }
 
-    .pf-c-input-group {
-      padding-top: var(--pf-global--spacer--md);
-      padding-bottom: var(--pf-global--spacer--md);
+  .pf-c-radio {
+    margin: var(--pf-global--spacer--sm) 0;
+    padding-left: var(--pf-global--spacer--sm);
+    &__label {
+      font-size: var(--pf-global--FontSize--sm);
     }
+  }
 
-    &__form {
-      margin: 0 var(--pf-global--spacer--lg);
+  &__form {
+    margin: 0 var(--pf-global--spacer--lg);
+  }
+
+  &__culler-input-group {
+    &.pf-c-input-group {
+      padding: var(--pf-global--spacer--xs) var(--pf-global--spacer--lg) 0;
+    }
+    .odh-number-input { 
+      max-width: 40px;
+      &__hour {
+        max-width: 60px;
+      }
     }
-  }
\ No newline at end of file
+  }
+}
\ No newline at end of file
diff --git a/frontend/src/pages/clusterSettings/ClusterSettings.tsx b/frontend/src/pages/clusterSettings/ClusterSettings.tsx
index e0492e1abf..23c114557d 100644
--- a/frontend/src/pages/clusterSettings/ClusterSettings.tsx
+++ b/frontend/src/pages/clusterSettings/ClusterSettings.tsx
@@ -3,15 +3,16 @@ import * as _ from 'lodash-es';
 import {
   Button,
   ButtonVariant,
-  Flex,
-  FlexItem,
   Form,
   FormGroup,
+  HelperText,
+  HelperTextItem,
   InputGroup,
   InputGroupText,
   InputGroupTextVariant,
   PageSection,
   PageSectionVariants,
+  Radio,
   Text,
   TextInput,
 } from '@patternfly/react-core';
@@ -20,23 +21,37 @@ import { fetchClusterSettings, updateClusterSettings } from '../../services/clus
 import { ClusterSettings } from '../../types';
 import { useDispatch } from 'react-redux';
 import { addNotification } from '../../redux/actions/actions';
+import {
+  DEFAULT_CONFIG,
+  DEFAULT_PVC_SIZE,
+  DEFAULT_CULLER_TIMEOUT,
+  MIN_PVC_SIZE,
+  MAX_PVC_SIZE,
+  CULLER_TIMEOUT_LIMITED,
+  CULLER_TIMEOUT_UNLIMITED,
+  MAX_MINUTE,
+  MIN_MINUTE,
+  MIN_HOUR,
+  MAX_HOUR,
+  DEFAULT_HOUR,
+} from './const';
+import { getTimeoutByHourAndMinute, getHourAndMinuteByTimeout } from '../../utilities/utils';
+
 import './ClusterSettings.scss';
 
 const description = `Update global settings for all users.`;
 
-const DEFAULT_PVC_SIZE = 20;
-const MIN_PVC_SIZE = 1;
-const MAX_PVC_SIZE = 16384;
-const DEFAULT_CONFIG: ClusterSettings = {
-  pvcSize: DEFAULT_PVC_SIZE,
-};
-
 const ClusterSettings: React.FC = () => {
   const isEmpty = false;
   const [loaded, setLoaded] = React.useState<boolean>(false);
   const [loadError, setLoadError] = React.useState<Error>();
   const [clusterSettings, setClusterSettings] = React.useState(DEFAULT_CONFIG);
-  const [pvcSize, setPvcSize] = React.useState<number>(DEFAULT_PVC_SIZE);
+  const [pvcSize, setPvcSize] = React.useState<number | string>(DEFAULT_PVC_SIZE);
+  const [cullerTimeoutChecked, setCullerTimeoutChecked] =
+    React.useState<string>(CULLER_TIMEOUT_UNLIMITED);
+  const [cullerTimeout, setCullerTimeout] = React.useState<number>(DEFAULT_CULLER_TIMEOUT);
+  const [hour, setHour] = React.useState<number>(DEFAULT_HOUR);
+  const [minute, setMinute] = React.useState<number>(0);
   const pvcDefaultBtnRef = React.useRef<HTMLButtonElement>();
   const dispatch = useDispatch();
 
@@ -47,39 +62,74 @@ const ClusterSettings: React.FC = () => {
         setLoadError(undefined);
         setClusterSettings(clusterSettings);
         setPvcSize(clusterSettings.pvcSize);
+        if (clusterSettings.cullerTimeout !== DEFAULT_CULLER_TIMEOUT) {
+          setCullerTimeoutChecked(CULLER_TIMEOUT_LIMITED);
+          setHour(getHourAndMinuteByTimeout(clusterSettings.cullerTimeout).hour);
+          setMinute(getHourAndMinuteByTimeout(clusterSettings.cullerTimeout).minute);
+        }
+        setCullerTimeout(clusterSettings.cullerTimeout);
       })
       .catch((e) => {
         setLoadError(e);
       });
   }, []);
 
+  React.useEffect(() => {
+    setCullerTimeout(getTimeoutByHourAndMinute(hour, minute));
+  }, [hour, minute]);
+
+  const radioCheckedChange = (_, event) => {
+    const { value } = event.currentTarget;
+    setCullerTimeoutChecked(value);
+    if (value === CULLER_TIMEOUT_UNLIMITED) {
+      setCullerTimeout(DEFAULT_CULLER_TIMEOUT);
+      submitClusterSettings({ pvcSize, cullerTimeout: DEFAULT_CULLER_TIMEOUT });
+    } else if (value === CULLER_TIMEOUT_LIMITED) {
+      setCullerTimeout(getTimeoutByHourAndMinute(hour, minute));
+      submitClusterSettings({ pvcSize, cullerTimeout: getTimeoutByHourAndMinute(hour, minute) });
+    }
+  };
+
+  const onEnterPress = (event) => {
+    if (event.key === 'Enter') {
+      if (pvcDefaultBtnRef.current) {
+        pvcDefaultBtnRef.current.focus();
+      }
+    }
+  };
+
   const submitClusterSettings = (newClusterSettings: ClusterSettings) => {
     if (!_.isEqual(clusterSettings, newClusterSettings)) {
-      updateClusterSettings(newClusterSettings)
-        .then((response) => {
-          if (response.success) {
-            setClusterSettings(newClusterSettings);
+      if (
+        Number(newClusterSettings?.pvcSize) !== 0 &&
+        Number(newClusterSettings?.cullerTimeout) !== 0
+      ) {
+        updateClusterSettings(newClusterSettings)
+          .then((response) => {
+            if (response.success) {
+              setClusterSettings(newClusterSettings);
+              dispatch(
+                addNotification({
+                  status: 'success',
+                  title: 'Cluster settings updated successfully.',
+                  timestamp: new Date(),
+                }),
+              );
+            } else {
+              throw new Error(response.error);
+            }
+          })
+          .catch((e) => {
             dispatch(
               addNotification({
-                status: 'success',
-                title: 'Cluster settings updated successfully.',
+                status: 'danger',
+                title: 'Error',
+                message: e.message,
                 timestamp: new Date(),
               }),
             );
-          } else {
-            throw new Error(response.error);
-          }
-        })
-        .catch((e) => {
-          dispatch(
-            addNotification({
-              status: 'danger',
-              title: 'Error',
-              message: e.message,
-              timestamp: new Date(),
-            }),
-          );
-        });
+          });
+      }
     }
   };
 
@@ -94,71 +144,168 @@ const ClusterSettings: React.FC = () => {
       emptyMessage="No cluster settings found."
     >
       {!isEmpty ? (
-        <div className="odh-cluster-settings">
-          <PageSection variant={PageSectionVariants.light} padding={{ default: 'noPadding' }}>
-            <Flex direction={{ default: 'column' }}>
-              <FlexItem>
-                <Form
-                  className="odh-cluster-settings__form"
-                  onSubmit={(e) => {
-                    e.preventDefault();
+        <PageSection
+          className="odh-cluster-settings"
+          variant={PageSectionVariants.light}
+          padding={{ default: 'noPadding' }}
+        >
+          <Form
+            className="odh-cluster-settings__form"
+            onSubmit={(e) => {
+              e.preventDefault();
+            }}
+          >
+            <FormGroup fieldId="pvc-size" label="PVC size">
+              <Text>
+                Changing the PVC size changes the storage size attached to the new notebook servers
+                for all users.
+              </Text>
+              <InputGroup>
+                <TextInput
+                  className="odh-number-input"
+                  name="pvc"
+                  id="pvc-size-input"
+                  type="text"
+                  aria-label="PVC Size Input"
+                  value={pvcSize}
+                  pattern="/^(\s*|\d+)$/"
+                  onBlur={() => {
+                    submitClusterSettings({ pvcSize: Number(pvcSize), cullerTimeout });
+                  }}
+                  onKeyPress={(event) => {
+                    if (event.key === 'Enter') {
+                      if (pvcDefaultBtnRef.current) pvcDefaultBtnRef.current.focus();
+                    }
+                  }}
+                  onChange={async (value: string) => {
+                    const modifiedValue = value.replace(/ /g, '');
+                    if (modifiedValue !== '') {
+                      let newValue = Number.isInteger(Number(modifiedValue))
+                        ? Number(modifiedValue)
+                        : pvcSize;
+                      newValue =
+                        newValue > MAX_PVC_SIZE
+                          ? MAX_PVC_SIZE
+                          : newValue < MIN_PVC_SIZE
+                          ? MIN_PVC_SIZE
+                          : newValue;
+                      setPvcSize(newValue);
+                    } else {
+                      setPvcSize(modifiedValue);
+                    }
+                  }}
+                />
+                <InputGroupText variant={InputGroupTextVariant.plain}>GiB</InputGroupText>
+              </InputGroup>
+              <Button
+                innerRef={pvcDefaultBtnRef}
+                variant={ButtonVariant.secondary}
+                onClick={() => {
+                  setPvcSize(DEFAULT_PVC_SIZE);
+                  submitClusterSettings({ pvcSize: DEFAULT_PVC_SIZE, cullerTimeout });
+                }}
+              >
+                Restore Default
+              </Button>
+              <HelperText>
+                <HelperTextItem
+                  variant={pvcSize === '' ? 'error' : 'indeterminate'}
+                  hasIcon={pvcSize === ''}
+                >
+                  Note: PVC size must be between 1 GiB and 16384 GiB.
+                </HelperTextItem>
+              </HelperText>
+            </FormGroup>
+            <FormGroup
+              fieldId="culler-timeout"
+              label="Stop idle notebooks"
+              helperText="All idle notebooks are stopped at cluster log out. To edit the cluster log
+                out time, discuss with your OpenShift Administrator to see if the OpenShift Authentication Timeout value can be modified."
+            >
+              <Text>Set the time limit for idle notebooks to be stopped.</Text>
+              <Radio
+                id="culler-timeout-unlimited"
+                label="Do not stop idle notebooks"
+                isChecked={cullerTimeoutChecked === CULLER_TIMEOUT_UNLIMITED}
+                name={CULLER_TIMEOUT_UNLIMITED}
+                onChange={radioCheckedChange}
+                value={CULLER_TIMEOUT_UNLIMITED}
+              />
+              <Radio
+                id="culler-timeout-limited"
+                label="Stop idle notebooks after"
+                isChecked={cullerTimeoutChecked === CULLER_TIMEOUT_LIMITED}
+                name={CULLER_TIMEOUT_LIMITED}
+                onChange={radioCheckedChange}
+                value={CULLER_TIMEOUT_LIMITED}
+              />
+              <InputGroup className="odh-cluster-settings__culler-input-group">
+                <TextInput
+                  className="odh-number-input__hour"
+                  name="hour"
+                  id="hour-input"
+                  type="text"
+                  aria-label="Culler Timeout Hour Input"
+                  value={hour}
+                  isDisabled={cullerTimeoutChecked === CULLER_TIMEOUT_UNLIMITED}
+                  onBlur={() => submitClusterSettings({ pvcSize, cullerTimeout })}
+                  onKeyPress={onEnterPress}
+                  onChange={(value: string) => {
+                    let newValue =
+                      isNaN(Number(value)) || !Number.isInteger(Number(value))
+                        ? hour
+                        : Number(value);
+                    newValue =
+                      newValue > MAX_HOUR ? MAX_HOUR : newValue < MIN_HOUR ? MIN_HOUR : newValue;
+                    // if the hour is max, then the minute can only be set to 0
+                    if (newValue === MAX_HOUR && minute !== MIN_MINUTE) {
+                      setMinute(MIN_MINUTE);
+                    }
+                    setHour(newValue);
+                  }}
+                />
+                <InputGroupText variant={InputGroupTextVariant.plain}>hours</InputGroupText>
+                <TextInput
+                  className="odh-number-input"
+                  name="minute"
+                  id="minute-input"
+                  type="text"
+                  aria-label="Culler Timeout Minute Input"
+                  value={minute}
+                  isDisabled={cullerTimeoutChecked === CULLER_TIMEOUT_UNLIMITED}
+                  onBlur={() => submitClusterSettings({ pvcSize, cullerTimeout })}
+                  onKeyPress={onEnterPress}
+                  onChange={(value: string) => {
+                    let newValue =
+                      isNaN(Number(value)) || !Number.isInteger(Number(value))
+                        ? minute
+                        : Number(value);
+                    newValue =
+                      newValue > MAX_MINUTE
+                        ? MAX_MINUTE
+                        : newValue < MIN_MINUTE
+                        ? MIN_MINUTE
+                        : newValue;
+                    // if the hour is max, then the minute can only be set to 0
+                    if (hour === MAX_HOUR) {
+                      newValue = MIN_MINUTE;
+                    }
+                    setMinute(newValue);
                   }}
+                />
+                <InputGroupText variant={InputGroupTextVariant.plain}>minutes</InputGroupText>
+              </InputGroup>
+              <HelperText>
+                <HelperTextItem
+                  variant={cullerTimeout === 0 ? 'error' : 'indeterminate'}
+                  hasIcon={cullerTimeout === 0}
                 >
-                  <FormGroup
-                    fieldId="pvc-size"
-                    label="PVC size"
-                    helperText="Note: The default size is 20 GiB."
-                  >
-                    <Text>
-                      Changing the PVC size changes the storage size attached to the new notebook
-                      servers for all users.
-                    </Text>
-                    <InputGroup>
-                      <TextInput
-                        className="odh-number-input"
-                        name="pvc"
-                        id="pvc-size-input"
-                        type="text"
-                        aria-label="PVC Size Input"
-                        value={pvcSize}
-                        pattern="[0-9]+"
-                        onBlur={() => submitClusterSettings({ pvcSize })}
-                        onKeyPress={(event) => {
-                          if (event.key === 'Enter') {
-                            if (pvcDefaultBtnRef.current) pvcDefaultBtnRef.current.focus();
-                          }
-                        }}
-                        onChange={async (value: string) => {
-                          let newValue = isNaN(Number(value)) ? pvcSize : Number(value);
-                          newValue =
-                            newValue > MAX_PVC_SIZE
-                              ? MAX_PVC_SIZE
-                              : newValue < MIN_PVC_SIZE
-                              ? MIN_PVC_SIZE
-                              : newValue;
-                          setPvcSize(newValue);
-                        }}
-                      />
-                      <InputGroupText id="plain-example" variant={InputGroupTextVariant.plain}>
-                        GiB
-                      </InputGroupText>
-                    </InputGroup>
-                    <Button
-                      innerRef={pvcDefaultBtnRef}
-                      variant={ButtonVariant.secondary}
-                      onClick={() => {
-                        setPvcSize(DEFAULT_PVC_SIZE);
-                        submitClusterSettings({ pvcSize: DEFAULT_PVC_SIZE });
-                      }}
-                    >
-                      Restore Defaults
-                    </Button>
-                  </FormGroup>
-                </Form>
-              </FlexItem>
-            </Flex>
-          </PageSection>
-        </div>
+                  Note: Notebook culler timeout must be between 1 minute and 1000 hours.
+                </HelperTextItem>
+              </HelperText>
+            </FormGroup>
+          </Form>
+        </PageSection>
       ) : null}
     </ApplicationsPage>
   );
diff --git a/frontend/src/pages/clusterSettings/const.ts b/frontend/src/pages/clusterSettings/const.ts
new file mode 100644
index 0000000000..ae1915863f
--- /dev/null
+++ b/frontend/src/pages/clusterSettings/const.ts
@@ -0,0 +1,17 @@
+import { ClusterSettings } from '../../types';
+
+export const DEFAULT_PVC_SIZE = 20;
+export const MIN_PVC_SIZE = 1;
+export const MAX_PVC_SIZE = 16384;
+export const CULLER_TIMEOUT_UNLIMITED = 'culler-unlimited-time';
+export const CULLER_TIMEOUT_LIMITED = 'culler-limited-time';
+export const DEFAULT_HOUR = 4;
+export const MAX_HOUR = 1000;
+export const MIN_HOUR = 0;
+export const MAX_MINUTE = 59;
+export const MIN_MINUTE = 0;
+export const DEFAULT_CULLER_TIMEOUT = 31536000; // 1 year as no culling
+export const DEFAULT_CONFIG: ClusterSettings = {
+  pvcSize: DEFAULT_PVC_SIZE,
+  cullerTimeout: DEFAULT_CULLER_TIMEOUT,
+};
diff --git a/frontend/src/services/clusterSettingsService.ts b/frontend/src/services/clusterSettingsService.ts
index 6c521b7b33..608436d495 100644
--- a/frontend/src/services/clusterSettingsService.ts
+++ b/frontend/src/services/clusterSettingsService.ts
@@ -20,6 +20,7 @@ export const updateClusterSettings = (
   const updateParams = new URLSearchParams();
 
   updateParams.set('pvcSize', `${settings.pvcSize}`);
+  updateParams.set('cullerTimeout', `${settings.cullerTimeout}`);
 
   const options = { params: updateParams };
   return axios
diff --git a/frontend/src/types.ts b/frontend/src/types.ts
index 11a84cc6f3..8837309487 100644
--- a/frontend/src/types.ts
+++ b/frontend/src/types.ts
@@ -9,7 +9,8 @@ export type DashboardConfig = {
 };
 
 export type ClusterSettings = {
-  pvcSize: number;
+  pvcSize: number | string;
+  cullerTimeout: number;
 };
 
 export type OdhApplication = {
diff --git a/frontend/src/utilities/utils.ts b/frontend/src/utilities/utils.ts
index c143b601da..6097765288 100644
--- a/frontend/src/utilities/utils.ts
+++ b/frontend/src/utilities/utils.ts
@@ -116,3 +116,13 @@ export const matchesSearch = (odhDoc: OdhDocument, filterText: string): boolean
     (description?.toLowerCase().includes(searchText) ?? false)
   );
 };
+
+export const getHourAndMinuteByTimeout = (timeout: number): { hour: number; minute: number } => {
+  const total_minutes = timeout / 60;
+  const hour = total_minutes / 60;
+  const minute = total_minutes % 60;
+  return { hour, minute };
+};
+
+export const getTimeoutByHourAndMinute = (hour: number, minute: number): number =>
+  (hour * 60 + minute) * 60;
diff --git a/install/dev-backend.sh b/install/dev-backend.sh
deleted file mode 100755
index ffbb9b7885..0000000000
--- a/install/dev-backend.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-printf "\n\n######## dev backend ########\n"
-
-DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
-
-cd ${DIR}/../backend
-pwd
-
-PORT=${BACKEND_DEV_PORT}
-npm install
-npm run start:dev
diff --git a/install/dev-frontend.sh b/install/dev-frontend.sh
deleted file mode 100755
index 85a0327f1a..0000000000
--- a/install/dev-frontend.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env bash
-printf "\n\n######## dev frontend ########\n"
-
-DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
-
-cd ${DIR}/../frontend
-pwd
-
-npm install
-npm run start:dev
diff --git a/install/dev.sh b/install/dev.sh
deleted file mode 100755
index 7c3401ffa0..0000000000
--- a/install/dev.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env bash
-printf "\n\n######## dev ########\n"
-
-DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
-
-ENV_FILE=${DIR}/../../.env.development
-
-if [ -f "${ENV_FILE}" ]; then
-  source ${ENV_FILE}
-  for ENV_VAR in $(sed 's/=.*//' ${ENV_FILE}); do export "${ENV_VAR}"; done
-fi
-
-PORT=${BACKEND_DEV_PORT}
-npm install
-npm run dev