diff --git a/package-lock.json b/package-lock.json
index 30dc0d07f..028d2a8aa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -30,6 +30,7 @@
         "react-intersection-observer": "^9.8.1",
         "react-json-view-lite": "^1.4.0",
         "react-virtuoso": "^4.10.4",
+        "server-only": "^0.0.1",
         "styletron-engine-monolithic": "^1.0.0",
         "styletron-react": "^6.1.1",
         "use-between": "^1.3.5",
@@ -9650,6 +9651,11 @@
         "node": ">=10"
       }
     },
+    "node_modules/server-only": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz",
+      "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="
+    },
     "node_modules/set-function-length": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz",
diff --git a/package.json b/package.json
index ccaf042ac..39459527d 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
     "react-intersection-observer": "^9.8.1",
     "react-json-view-lite": "^1.4.0",
     "react-virtuoso": "^4.10.4",
+    "server-only": "^0.0.1",
     "styletron-engine-monolithic": "^1.0.0",
     "styletron-react": "^6.1.1",
     "use-between": "^1.3.5",
diff --git a/src/config/dynamic/dynamic.config.ts b/src/config/dynamic/dynamic.config.ts
new file mode 100644
index 000000000..4f1321946
--- /dev/null
+++ b/src/config/dynamic/dynamic.config.ts
@@ -0,0 +1,47 @@
+import 'server-only';
+
+import type {
+  ConfigAsyncResolverDefinition,
+  ConfigEnvDefinition,
+  ConfigSyncResolverDefinition,
+} from '../../utils/config/config.types';
+
+const dynamicConfigs: {
+  CADENCE_WEB_PORT: ConfigEnvDefinition;
+  ADMIN_SECURITY_TOKEN: ConfigEnvDefinition;
+  GRPC_PROTO_DIR_BASE_PATH: ConfigEnvDefinition;
+  GRPC_SERVICES_NAMES: ConfigEnvDefinition;
+  COMPUTED: ConfigSyncResolverDefinition<[string], [string]>;
+  DYNAMIC: ConfigAsyncResolverDefinition<undefined, number>;
+} = {
+  CADENCE_WEB_PORT: {
+    env: 'CADENCE_WEB_PORT',
+    //Fallback to nextjs default port if CADENCE_WEB_PORT is not provided
+    default: '3000',
+  },
+  ADMIN_SECURITY_TOKEN: {
+    env: 'CADENCE_ADMIN_SECURITY_TOKEN',
+    default: '',
+  },
+  GRPC_PROTO_DIR_BASE_PATH: {
+    env: 'GRPC_PROTO_DIR_BASE_PATH',
+    default: 'src/__generated__/idl/proto',
+  },
+  GRPC_SERVICES_NAMES: {
+    env: 'NEXT_PUBLIC_CADENCE_GRPC_SERVICES_NAMES',
+    default: 'cadence-frontend',
+  },
+  // For testing purposes
+  DYNAMIC: {
+    resolver: async () => {
+      return 1;
+    },
+  },
+  COMPUTED: {
+    resolver: (value: [string]) => {
+      return value;
+    },
+  },
+} as const;
+
+export default dynamicConfigs;
diff --git a/src/instrumentation.ts b/src/instrumentation.ts
index f80ee9863..82973eb1b 100644
--- a/src/instrumentation.ts
+++ b/src/instrumentation.ts
@@ -1,5 +1,10 @@
+import getTransformedConfigs from './utils/config/get-transformed-configs';
+import { setLoadedGlobalConfigs } from './utils/config/global-configs-ref';
 import { registerLoggers } from './utils/logger';
 
 export async function register() {
   registerLoggers();
+  if (process.env.NEXT_RUNTIME === 'nodejs') {
+    setLoadedGlobalConfigs(getTransformedConfigs());
+  }
 }
diff --git a/src/utils/config/__tests__/get-config-value.node.ts b/src/utils/config/__tests__/get-config-value.node.ts
new file mode 100644
index 000000000..29764a9ec
--- /dev/null
+++ b/src/utils/config/__tests__/get-config-value.node.ts
@@ -0,0 +1,30 @@
+import { type LoadedConfigs } from '../config.types';
+import getConfigValue from '../get-config-value';
+import { loadedGlobalConfigs } from '../global-configs-ref';
+
+jest.mock('../global-configs-ref', () => ({
+  loadedGlobalConfigs: {
+    COMPUTED: jest.fn(),
+    CADENCE_WEB_PORT: 'someValue',
+  } satisfies Partial<LoadedConfigs>,
+}));
+
+describe('getConfigValue', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  it('returns the value directly if it is not a function', async () => {
+    const result = await getConfigValue('CADENCE_WEB_PORT');
+    expect(result).toBe('someValue');
+  });
+
+  it('calls the function with the provided argument and returns the result', async () => {
+    const mockFn = loadedGlobalConfigs.COMPUTED as jest.Mock;
+    mockFn.mockResolvedValue('resolvedValue');
+
+    const result = await getConfigValue('COMPUTED', ['arg']);
+    expect(mockFn).toHaveBeenCalledWith(['arg']);
+    expect(result).toBe('resolvedValue');
+  });
+});
diff --git a/src/utils/config/__tests__/get-config-value.test.ts b/src/utils/config/__tests__/get-config-value.test.ts
new file mode 100644
index 000000000..a0f9d1cf4
--- /dev/null
+++ b/src/utils/config/__tests__/get-config-value.test.ts
@@ -0,0 +1,21 @@
+import getConfigValue from '../get-config-value';
+
+jest.mock('../global-configs-ref', () => ({
+  loadedGlobalConfigs: {
+    CADENCE_WEB_PORT: 'someValue',
+  },
+}));
+
+describe('getConfigValue', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  it('throws an error when invoked in the browser', async () => {
+    (global as any).window = {};
+    await expect(getConfigValue('CADENCE_WEB_PORT', undefined)).rejects.toThrow(
+      'getConfigValue cannot be invoked on browser'
+    );
+    delete (global as any).window;
+  });
+});
diff --git a/src/utils/config/__tests__/get-transformed-configs.test.ts b/src/utils/config/__tests__/get-transformed-configs.test.ts
new file mode 100644
index 000000000..07f9af10e
--- /dev/null
+++ b/src/utils/config/__tests__/get-transformed-configs.test.ts
@@ -0,0 +1,61 @@
+import {
+  type ConfigEnvDefinition,
+  type ConfigSyncResolverDefinition,
+} from '../config.types';
+import transformConfigs from '../transform-configs';
+
+describe('transformConfigs', () => {
+  const originalEnv = process.env;
+  beforeEach(() => {
+    jest.resetModules();
+    process.env = {
+      ...originalEnv,
+      $$$_MOCK_ENV_CONFIG1: 'envValue1',
+    };
+  });
+
+  afterEach(() => {
+    process.env = originalEnv;
+  });
+
+  it('should add resolver function as is', () => {
+    const configDefinitions: {
+      config1: ConfigEnvDefinition;
+      config2: ConfigSyncResolverDefinition<undefined, string>;
+    } = {
+      config1: { env: '$$$_MOCK_ENV_CONFIG1', default: 'default1' },
+      config2: {
+        resolver: () => 'resolvedValue',
+      },
+    };
+    const result = transformConfigs(configDefinitions);
+    expect(result).toEqual({
+      config1: 'envValue1',
+      config2: configDefinitions.config2.resolver,
+    });
+  });
+
+  it('should return environment variable value when present', () => {
+    const configDefinitions: {
+      config1: ConfigEnvDefinition;
+    } = {
+      config1: { env: '$$$_MOCK_ENV_CONFIG1', default: 'default1' },
+    };
+    const result = transformConfigs(configDefinitions);
+    expect(result).toEqual({
+      config1: 'envValue1',
+    });
+  });
+
+  it('should return default value when environment variable is not present', () => {
+    const configDefinitions: {
+      config3: ConfigEnvDefinition;
+    } = {
+      config3: { env: '$$$_MOCK_ENV_CONFIG3', default: 'default3' },
+    };
+    const result = transformConfigs(configDefinitions);
+    expect(result).toEqual({
+      config3: 'default3',
+    });
+  });
+});
diff --git a/src/utils/config/__tests__/global-ref.node.ts b/src/utils/config/__tests__/global-ref.node.ts
new file mode 100644
index 000000000..fcdf28215
--- /dev/null
+++ b/src/utils/config/__tests__/global-ref.node.ts
@@ -0,0 +1,51 @@
+import GlobalRef from '../global-ref';
+
+describe('GlobalRef', () => {
+  let originalGlobal: any;
+
+  beforeEach(() => {
+    originalGlobal = global;
+    global = { ...global };
+  });
+
+  afterEach(() => {
+    global = originalGlobal;
+  });
+
+  it('should set and get the value correctly', () => {
+    const globalRef = new GlobalRef<number>('test-unique-name');
+    globalRef.value = 42;
+    expect(globalRef.value).toBe(42);
+  });
+
+  it('should return undefined if value is not set', () => {
+    const globalRef = new GlobalRef<number>('another-unique-name');
+    expect(globalRef.value).toBeUndefined();
+  });
+
+  it('should handle different types of values', () => {
+    const stringRef = new GlobalRef<string>('string-unique-name');
+    stringRef.value = 'test string';
+    expect(stringRef.value).toBe('test string');
+
+    const objectRef = new GlobalRef<{ key: string }>('object-unique-name');
+    objectRef.value = { key: 'value' };
+    expect(objectRef.value).toEqual({ key: 'value' });
+  });
+
+  it('should use the same symbol for the same unique name', () => {
+    const ref1 = new GlobalRef<number>('shared-unique-name');
+    const ref2 = new GlobalRef<number>('shared-unique-name');
+    ref1.value = 100;
+    expect(ref2.value).toBe(100);
+  });
+
+  it('should use different symbols for different unique names', () => {
+    const ref1 = new GlobalRef<number>('unique-name-1');
+    const ref2 = new GlobalRef<number>('unique-name-2');
+    ref1.value = 100;
+    ref2.value = 200;
+    expect(ref1.value).toBe(100);
+    expect(ref2.value).toBe(200);
+  });
+});
diff --git a/src/utils/config/__tests__/transform-configs.test.ts b/src/utils/config/__tests__/transform-configs.test.ts
new file mode 100644
index 000000000..7980ad016
--- /dev/null
+++ b/src/utils/config/__tests__/transform-configs.test.ts
@@ -0,0 +1,39 @@
+import { type ConfigEnvDefinition, type LoadedConfigs } from '../config.types';
+import { default as getTransformedConfigs } from '../get-transformed-configs';
+
+type MockConfigDefinitions = {
+  config1: ConfigEnvDefinition;
+  config2: ConfigEnvDefinition;
+};
+jest.mock(
+  '@/config/dynamic/dynamic.config',
+  () =>
+    ({
+      config1: { env: '$$$_MOCK_ENV_CONFIG1', default: 'default1' },
+      config2: { env: '$$$_MOCK_ENV_CONFIG2', default: 'default2' },
+    }) satisfies MockConfigDefinitions
+);
+
+describe('getTransformedConfigs', () => {
+  const originalEnv = process.env;
+  beforeEach(() => {
+    jest.resetModules();
+    process.env = {
+      ...originalEnv,
+      $$$_MOCK_ENV_CONFIG1: 'envValue1',
+      $$$_MOCK_ENV_CONFIG2: '',
+    };
+  });
+
+  afterEach(() => {
+    process.env = originalEnv;
+  });
+
+  it('should return transformed dynamic configs', () => {
+    const result = getTransformedConfigs();
+    expect(result).toEqual({
+      config1: 'envValue1',
+      config2: 'default2',
+    } satisfies LoadedConfigs<MockConfigDefinitions>);
+  });
+});
diff --git a/src/utils/config/config.types.ts b/src/utils/config/config.types.ts
new file mode 100644
index 000000000..ec1139a3e
--- /dev/null
+++ b/src/utils/config/config.types.ts
@@ -0,0 +1,80 @@
+import { type z } from 'zod';
+
+import type dynamicConfigs from '@/config/dynamic/dynamic.config';
+
+export type ConfigAsyncResolverDefinition<Args, ReturnType> = {
+  resolver: (args: Args) => Promise<ReturnType>;
+  // isPublic?: boolean; // would be implemented in upcoming PR
+};
+
+export type ConfigSyncResolverDefinition<Args, ReturnType> = {
+  resolver: (args: Args) => ReturnType;
+  // forceSync?: boolean; // would be replaced in upcoming PR
+  // isPublic?: boolean; // would be implemented in upcoming PR
+};
+
+export type ConfigEnvDefinition = {
+  env: string;
+  default: string;
+  // forceSync?: boolean; // would be replaced in upcoming PR
+  // isPublic?: boolean; // would be implemented in upcoming PR
+};
+
+export type ConfigDefinition =
+  | ConfigAsyncResolverDefinition<any, any>
+  | ConfigSyncResolverDefinition<any, any>
+  | ConfigEnvDefinition;
+
+export type ConfigDefinitionRecords = Record<string, ConfigDefinition>;
+
+type InferLoadedConfig<T extends Record<string, any>> = {
+  [K in keyof T]: T[K] extends ConfigEnvDefinition
+    ? string // If it's an env definition, the value is a string
+    : T[K] extends ConfigSyncResolverDefinition<infer Args, infer ReturnType>
+      ? (args: Args) => ReturnType // If it's a sync resolver, it's a function with matching signature
+      : T[K] extends ConfigAsyncResolverDefinition<infer Args, infer ReturnType>
+        ? (args: Args) => Promise<ReturnType> // If it's an async resolver, it's a promise-returning function
+        : never; // If it doesn't match any known type, it's never
+};
+
+export type LoadedConfigs<
+  C extends ConfigDefinitionRecords = typeof dynamicConfigs,
+> = InferLoadedConfig<C>;
+
+export type ArgOfConfigResolver<K extends keyof LoadedConfigs> =
+  LoadedConfigs[K] extends (args: any) => any
+    ? Parameters<LoadedConfigs[K]>[0]
+    : undefined;
+
+export type LoadedConfigValue<K extends keyof LoadedConfigs> =
+  LoadedConfigs[K] extends (args: any) => any
+    ? ReturnType<LoadedConfigs[K]>
+    : string;
+
+export type ConfigKeysWithArgs = {
+  [K in keyof LoadedConfigs]: LoadedConfigs[K] extends (args: undefined) => any
+    ? never
+    : LoadedConfigs[K] extends (args: any) => any
+      ? K
+      : never;
+}[keyof LoadedConfigs];
+
+export type ConfigKeysWithoutArgs = Exclude<
+  keyof LoadedConfigs,
+  ConfigKeysWithArgs
+>;
+
+type ResolverType<Args, ReturnType> =
+  | ConfigSyncResolverDefinition<Args, ReturnType>
+  | ConfigAsyncResolverDefinition<Args, ReturnType>;
+
+export type InferResolverSchema<Definitions extends Record<string, any>> = {
+  [Key in keyof Definitions]: Definitions[Key] extends ResolverType<
+    infer Args,
+    infer ReturnType
+  >
+    ? { args: z.ZodType<Args>; returnType: z.ZodType<ReturnType> }
+    : never;
+};
+
+export type ResolverSchemas = InferResolverSchema<typeof dynamicConfigs>;
diff --git a/src/utils/config/get-config-value.ts b/src/utils/config/get-config-value.ts
new file mode 100644
index 000000000..0d0102a0b
--- /dev/null
+++ b/src/utils/config/get-config-value.ts
@@ -0,0 +1,37 @@
+import type {
+  LoadedConfigValue,
+  LoadedConfigs,
+  ArgOfConfigResolver,
+  ConfigKeysWithArgs,
+  ConfigKeysWithoutArgs,
+} from './config.types';
+import { loadedGlobalConfigs } from './global-configs-ref';
+
+// Overload for keys requiring arguments
+export default async function getConfigValue<K extends ConfigKeysWithArgs>(
+  key: K,
+  arg: ArgOfConfigResolver<K>
+): Promise<LoadedConfigValue<K>>;
+
+// Overload for keys not requiring arguments (env configs)
+export default async function getConfigValue<K extends ConfigKeysWithoutArgs>(
+  key: K,
+  arg?: undefined
+): Promise<LoadedConfigValue<K>>;
+
+export default async function getConfigValue<K extends keyof LoadedConfigs>(
+  key: K,
+  arg?: ArgOfConfigResolver<K>
+): Promise<LoadedConfigValue<K>> {
+  if (typeof window !== 'undefined') {
+    throw new Error('getConfigValue cannot be invoked on browser');
+  }
+
+  const value = loadedGlobalConfigs[key] as LoadedConfigs[K];
+
+  if (typeof value === 'function') {
+    return (await value(arg)) as LoadedConfigValue<K>;
+  }
+
+  return value as LoadedConfigValue<K>;
+}
diff --git a/src/utils/config/get-transformed-configs.ts b/src/utils/config/get-transformed-configs.ts
new file mode 100644
index 000000000..777df4aba
--- /dev/null
+++ b/src/utils/config/get-transformed-configs.ts
@@ -0,0 +1,12 @@
+import 'server-only';
+
+import configDefinitions from '../../config/dynamic/dynamic.config';
+
+import type { LoadedConfigs } from './config.types';
+import transformConfigs from './transform-configs';
+
+export default function getTransformedConfigs(): LoadedConfigs<
+  typeof configDefinitions
+> {
+  return transformConfigs(configDefinitions);
+}
diff --git a/src/utils/config/global-configs-ref.ts b/src/utils/config/global-configs-ref.ts
new file mode 100644
index 000000000..33c7ae87b
--- /dev/null
+++ b/src/utils/config/global-configs-ref.ts
@@ -0,0 +1,10 @@
+import { type LoadedConfigs } from './config.types';
+import GlobalRef from './global-ref';
+
+const globalConfigRef = new GlobalRef<LoadedConfigs>('cadence-config');
+const setLoadedGlobalConfigs = (c: LoadedConfigs): void => {
+  globalConfigRef.value = c;
+};
+
+const loadedGlobalConfigs: LoadedConfigs = globalConfigRef.value;
+export { loadedGlobalConfigs, setLoadedGlobalConfigs };
diff --git a/src/utils/config/global-ref.ts b/src/utils/config/global-ref.ts
new file mode 100644
index 000000000..48f855592
--- /dev/null
+++ b/src/utils/config/global-ref.ts
@@ -0,0 +1,15 @@
+export default class GlobalRef<T> {
+  private readonly sym: symbol;
+
+  constructor(uniqueName: string) {
+    this.sym = Symbol.for(uniqueName);
+  }
+
+  get value() {
+    return (global as any)[this.sym] as T;
+  }
+
+  set value(value: T) {
+    (global as any)[this.sym] = value;
+  }
+}
diff --git a/src/utils/config/transform-configs.ts b/src/utils/config/transform-configs.ts
new file mode 100644
index 000000000..1d19b2f39
--- /dev/null
+++ b/src/utils/config/transform-configs.ts
@@ -0,0 +1,20 @@
+import 'server-only';
+
+import type { ConfigDefinitionRecords, LoadedConfigs } from './config.types';
+
+export default function transformConfigs<C extends ConfigDefinitionRecords>(
+  configDefinitions: C
+): LoadedConfigs<C> {
+  const resolvedConfig = Object.fromEntries(
+    Object.entries(configDefinitions).map(([key, definition]) => {
+      if ('resolver' in definition) {
+        return [key, definition.resolver];
+      }
+
+      const envValue = (process.env[definition.env] || '').trim();
+      return [key, envValue === '' ? definition.default : envValue];
+    })
+  );
+
+  return resolvedConfig;
+}