# How to Set Up Environment Variables in Retool

- Tool: Retool
- Difficulty: Intermediate
- Time required: 15-20 min
- Compatibility: Retool Cloud and Self-hosted
- Last updated: March 2026

## TL;DR

Retool configuration variables (configVars) are admin-managed key-value pairs stored in Settings → Configuration Variables. Access them in apps via `{{ retoolContext.configVars.VARIABLE_NAME }}` and in JS queries via `retoolContext.configVars.VARIABLE_NAME`. Secret variables are encrypted and never exposed client-side. Per-environment values let staging and production use different API keys or base URLs without changing app code.

## Configuration Variables: Admin-Managed App Settings

Retool provides two distinct variable systems that are often confused: runtime temporary state variables (created in the app editor, session-scoped) and configuration variables (configVars — admin-managed, persistent, per-environment).

Configuration variables are created by Retool admins in Settings → Configuration Variables. They're equivalent to environment variables in a traditional application: values that change between environments (staging vs. production) but don't change during app runtime. Common uses: API base URLs, feature flags, per-environment rate limits, and non-secret configuration.

For secrets (API keys, database passwords), Retool provides Secret configuration variables — these are encrypted at rest, never transmitted to the browser, and only accessible from server-side contexts like Resource configurations.

This tutorial covers creating configVars, accessing them in apps and queries, setting per-environment values, and the important distinction between secret and non-secret vars.

## Before you start

- Retool Admin access (required to manage Configuration Variables in Settings)
- Understanding of what needs to vary between environments in your app
- A Retool app that will consume the configuration variables
- Knowledge of which values should be secret (API keys) vs. non-secret (base URLs)

## Step-by-step guide

### 1. Navigate to Configuration Variables

Go to the Retool home page → Settings (top-right user menu) → Configuration Variables. This page is only accessible to Retool Admins. Here you see a table of all existing configuration variables with columns for Name, Value, Environment, and whether the variable is a Secret.

If you're on a plan with multiple environments (staging + production), you'll see separate rows per environment for the same variable name. If you're on a basic plan with one environment, you'll see single rows.

**Expected result:** You're on the Configuration Variables page and can see any existing variables or an empty table if none exist.

### 2. Create a non-secret configuration variable

Click 'Create Variable' (or '+ Add'). Fill in:

- Name: Use SCREAMING_SNAKE_CASE (e.g., API_BASE_URL, FEATURE_FLAGS, MAX_PAGE_SIZE)
- Value: The configuration value for this environment
- Secret: Leave unchecked for non-sensitive values (base URLs, feature flag strings, etc.)
- Environment: Select 'Staging' or 'Production' (if you have multiple environments configured)

Non-secret config vars are transmitted to the browser and accessible in `{{ }}` expressions. Don't put API keys or passwords in non-secret vars.

```
// Example non-secret config vars:
// Name: API_BASE_URL
// Value (staging): https://api.staging.yourcompany.com
// Value (production): https://api.yourcompany.com

// Name: MAX_EXPORT_ROWS
// Value: 5000

// Name: FEATURE_NEW_DASHBOARD
// Value (staging): true
// Value (production): false

// Access in app {{ }} expressions:
{{ retoolContext.configVars.API_BASE_URL }}
{{ retoolContext.configVars.FEATURE_NEW_DASHBOARD === 'true' }}
{{ parseInt(retoolContext.configVars.MAX_EXPORT_ROWS) }}
```

**Expected result:** The new configuration variable appears in the table and is accessible via retoolContext.configVars.

### 3. Create a secret configuration variable

For sensitive values (API keys, webhook secrets, database credentials), check the 'Secret' checkbox when creating the variable. Secret vars behave differently:

- Value is encrypted at rest in Retool's database
- Value is NEVER transmitted to the browser — you cannot read it in {{ }} expressions
- Only accessible in Resource configurations (as `{{ config.variableName }}`) and server-side contexts
- The value field shows as *** after saving

Secret vars are the correct place for Stripe API keys, SendGrid API keys, Slack tokens, and any value that would be dangerous if exposed in browser DevTools.

**Expected result:** The secret variable appears in the list with a *** value indicator. It cannot be read back in the UI after saving.

### 4. Access config vars in JS queries

In any Retool JS query, access configuration variables through the retoolContext object. This works in both editor and preview modes, and correctly returns the per-environment value based on the current environment selector.

```
// JS Query: callExternalAPI
// Using configVars for base URL and API key reference

const baseUrl = retoolContext.configVars.API_BASE_URL;
const maxRows = parseInt(retoolContext.configVars.MAX_EXPORT_ROWS) || 1000;

// Log current environment for debugging (remove before release)
console.log('Environment:', retoolContext.environment);
console.log('Base URL:', baseUrl);

// Use configVar in a fetch call:
const response = await fetch(`${baseUrl}/orders?limit=${maxRows}`, {
  headers: {
    'Content-Type': 'application/json'
    // NOTE: Never put API keys in client-side fetch headers
    // Use a Retool REST resource with auth configured instead
  }
});

return await response.json();

// Feature flag check:
const showNewDashboard = retoolContext.configVars.FEATURE_NEW_DASHBOARD === 'true';
if (showNewDashboard) {
  return await newDashboardQuery.trigger();
} else {
  return await legacyDashboardQuery.trigger();
}
```

**Expected result:** The JS query reads config vars from retoolContext and uses the environment-appropriate value.

### 5. Use config vars in Resource configurations

Config vars (including secrets) can be referenced in Resource configurations. Go to Settings → Resources → select a resource → Edit. In the resource configuration fields (base URL, API key, etc.), use the `{{ config.variableName }}` syntax to reference a config var.

This is the preferred way to use secret API keys: they live in the resource configuration, encrypted by Retool, and are used server-side when queries execute.

```
// In Resource configuration fields (not in app code):
// REST API resource → Base URL field:
https://api.example.com

// REST API resource → Headers → Authorization:
Bearer {{ config.EXTERNAL_API_KEY }}

// Database resource → Password field:
{{ config.DB_PASSWORD }}

// NOTE: The {{ config.VAR }} syntax is only valid inside Resource configuration fields.
// In JS queries and app expressions, use retoolContext.configVars.VAR_NAME instead.
```

**Expected result:** Resources use encrypted secret configVars in their configuration without exposing credentials in app code.

### 6. Set per-environment values for the same variable

If your Retool organization has multiple environments configured (e.g., Staging and Production), you can create the same config var with different values per environment. This is the most powerful aspect of configVars.

In Configuration Variables, create the same variable name twice:
- API_BASE_URL (Environment: Staging) = https://api.staging.yourcompany.com
- API_BASE_URL (Environment: Production) = https://api.yourcompany.com

When users run the app in staging, `retoolContext.configVars.API_BASE_URL` returns the staging URL. In production, it returns the production URL. Zero app code changes required.

```
// No code change needed in the app — the same expression:
{{ retoolContext.configVars.API_BASE_URL }}

// ...automatically returns the environment-appropriate value.

// You can also check the current environment explicitly:
// In an app expression:
{{ retoolContext.environment }}
// Returns: 'production', 'staging', or 'custom environment name'

// Conditional logic based on environment:
{{ retoolContext.environment === 'production' 
   ? 'Real payment processing enabled' 
   : 'TEST MODE: Using Stripe test keys' }}
```

**Expected result:** The same app expression returns different values when run in staging vs. production without any code changes.

## Complete code example

File: `JS Query: getAppConfig`

```javascript
// JS Query: getAppConfig
// Returns all non-secret config vars with environment context
// Useful for debugging configuration issues

const environment = retoolContext.environment;
const configVars = retoolContext.configVars;

// Build a configuration summary (non-secret vars only)
const config = {
  environment,
  apiBaseUrl: configVars.API_BASE_URL || 'NOT SET',
  maxExportRows: parseInt(configVars.MAX_EXPORT_ROWS) || 1000,
  featureNewDashboard: configVars.FEATURE_NEW_DASHBOARD === 'true',
  featureAnalytics: configVars.FEATURE_ANALYTICS === 'true',
  supportEmail: configVars.SUPPORT_EMAIL || 'support@yourcompany.com',
  appVersion: configVars.APP_VERSION || '1.0.0',
};

// Environment-specific warnings
const warnings = [];
if (environment === 'staging') {
  warnings.push('Running in STAGING environment — using test data');
}
if (!configVars.API_BASE_URL) {
  warnings.push('WARNING: API_BASE_URL config var not set');
}

return {
  config,
  warnings,
  timestamp: new Date().toISOString()
};
```

## Common mistakes

- **Trying to read a secret configVar in a {{ }} expression or client-side JS query and getting undefined** — undefined Fix: Secret config vars are only accessible in Resource configurations (using config.VAR_NAME). They are intentionally never sent to the browser. Move API key usage into a Resource configuration field, or make the variable non-secret if it's acceptable to expose in DevTools.
- **Creating config vars only for production and forgetting to create them for staging, causing queries to fail in the staging environment** — undefined Fix: For every config var, create rows for ALL environments. When a config var doesn't exist for an environment, retoolContext.configVars.VAR_NAME returns undefined — this can cause runtime errors. Always create both staging and production values.
- **Confusing Retool configVars with Retool temporary state variables and using setState() to try to set a configVar** — undefined Fix: ConfigVars are admin-managed, static settings — you cannot change them at runtime from app code. Temporary state variables (created in the editor's State tab) are runtime, session-scoped values set with await stateVar.setValue(). These are completely different systems.

## Best practices

- Use SCREAMING_SNAKE_CASE for all config var names to distinguish them from runtime variables in {{ }} expressions
- Never store API keys or passwords in non-secret config vars — they're transmitted to the browser and visible in DevTools
- Prefix feature flags with FEATURE_ and boolean strings as 'true'/'false' to make them self-documenting
- Create all config vars for all environments at once — a missing config var in production causes silent failures
- Document config vars in a README or the Retool app description so future admins know what each one is for
- Use config. prefix in Resource configurations (server-side, secure) and retoolContext.configVars. prefix in app expressions (client-side, non-secret only)
- Run getAppConfig JS query in staging to verify all vars are set before deploying to production

## Frequently asked questions

### What is the difference between Retool configVars and Retool temporary state variables?

ConfigVars (Configuration Variables) are admin-managed, persistent key-value settings stored in Settings → Configuration Variables. They have per-environment values and are set once by admins — not changed at runtime. Temporary state variables are created in the app editor, are session-scoped (reset on page reload), and are changed dynamically during app use with await stateVar.setValue(). ConfigVars = infrastructure config; state vars = runtime app state.

### Can Retool configVars be modified from within an app (by a non-admin user)?

No. Configuration variables can only be created and modified in Settings → Configuration Variables, which requires Retool Admin access. Regular app users cannot change configVars. If you need user-editable configuration, use Temporary State or Global variables, or store configuration in a database table that your app reads and writes.

### How do I use a Retool configVar in a SQL query's WHERE clause?

Reference configVars in SQL using the standard Retool {{ }} expression syntax: SELECT * FROM orders WHERE region = {{ retoolContext.configVars.DEFAULT_REGION }} LIMIT {{ retoolContext.configVars.MAX_ROWS }}. Note that configVars are always strings — cast numeric configVars in SQL: LIMIT {{ parseInt(retoolContext.configVars.MAX_ROWS) || 100 }}.

---

Source: https://www.rapidevelopers.com/retool-tutorial/how-to-set-up-environment-variables-in-retool
© RapidDev — https://www.rapidevelopers.com/retool-tutorial/how-to-set-up-environment-variables-in-retool
