1---
2title: Environment variables and secrets
3description: Learn how to use environment variables and secrets in an EAS Build.
4---
5
6import { Collapsible } from '~/ui/components/Collapsible';
7import { Step } from '~/ui/components/Step';
8import { Terminal } from '~/ui/components/Snippet';
9
10The [Environment variables in Expo](/guides/environment-variables) guide presents several options for accessing system environment variables in your app's JavaScript code. This can be a useful way to inject values in your code, but [these values should not be secrets](/guides/environment-variables#security-considerations), and so the value it provides can be summarized as a convenience for accommodating certain development workflows.
11
12Using the techniques described in the environment variables document above, environment variables are inlined (the `process.env.X` text is replaced with its evaluated result) in your app's JavaScript code _at the time that the app is built_, and included in the app bundle. This means that the substitution would occur on EAS Build servers and not on your development machine, so if you tried to run a build on EAS Build without explicitly providing values or fallbacks for the environment variables, then you are likely to encounter either a build-time or runtime error.
13
14## Using plaintext environment variables
15
16Plaintext environment variables are strings that you are comfortable with committing to your source control and using inside of your client app code. For example, you might use an `API_URL` variable to tell your app what backend to use.
17
18You can specify environment variables for specific build jobs using **eas.json**:
19
20```json eas.json
21{
22  "build": {
23    "production": {
24      "env": {
25        "API_URL": "https://api.production.com"
26      }
27    }
28  }
29}
30```
31
32You can access these variables in your application using the techniques described in the ["Environment variables in Expo"](/guides/environment-variables) guide. You can also share common configurations between different build profiles using the `"extends"` property, if both profiles have an `env` object defined, the content will be merged.
33
34```json eas.json
35{
36  "build": {
37    "production": {
38      "env": {
39        "API_URL": "https://api.production.com"
40      }
41    },
42    "test": {
43      "distribution": "internal",
44      "extends": "production"
45    }
46  }
47}
48```
49
50See the [eas.json reference](/build/eas-json) for more information.
51
52### Setting environment variables dynamically
53
54You can also set environment variables dynamically during the build process. The `set-env` executable is available in the `PATH` on EAS Build workers, and can be used to set environment variables that will be visible in the next build phases.
55
56For example, you can add the following in one of the [EAS Build hooks](/build-reference/npm-hooks/) and the environment variable `EXAMPLE_ENV` will be available until the end of the build job.
57
58<Terminal cmd={[ 'set-env EXAMPLE_ENV "example value"' ]} />
59
60## Environment variables and app.config.js
61
62Environment variables used in your build profile will also be used to evaluate **app.config.js** when you run `eas build`. This is important to ensure that the result of evaluating **app.config.js** is the same when it's done locally while initiating the build (to gather metadata for the build job) and when it occurs on the remote builder, for example, to configure the project during `npx expo prebuild` or to embed the configuration data in the app.
63
64## Built-in environment variables
65
66The following environment variables are exposed to each build job &mdash; they are not set when evaluating **app.config.js** locally:
67
68- `CI=1` - indicates this is a CI environment
69- `EAS_BUILD=true` - indicates this is an EAS Build environment
70- `EAS_BUILD_PLATFORM` - either `android` or `ios`
71- `EAS_BUILD_RUNNER` - either `eas-build` for EAS Build cloud builds or `local-build-plugin` for [local builds](local-builds)
72- `EAS_BUILD_ID` - the build ID, e.g. `f51831f0-ea30-406a-8c5f-f8e1cc57d39c`
73- `EAS_BUILD_PROFILE` - the name of the build profile from **eas.json**, e.g. `production`
74- `EAS_BUILD_GIT_COMMIT_HASH` - the hash of the Git commit, e.g. `88f28ab5ea39108ade978de2d0d1adeedf0ece76`
75- `EAS_BUILD_NPM_CACHE_URL` - the URL of npm cache ([learn more](/build-reference/private-npm-packages))
76- `EAS_BUILD_MAVEN_CACHE_URL` - the URL of Maven cache ([learn more](/build-reference/caching/#android-dependencies))
77- `EAS_BUILD_COCOAPODS_CACHE_URL` - the URL of CocoaPods cache ([learn more](/build-reference/caching/#ios-dependencies))
78- `EAS_BUILD_USERNAME` - the username of the user initiating the build (it's undefined for bot users)
79- `EAS_BUILD_WORKINGDIR` - the remote directory path with your project
80
81## Using secrets in environment variables
82
83To provide your build jobs with access to values that are too sensitive to include in your source code and Git repository, you can use "Secrets".
84
85A secret is made up of a name and a value. The name can only contain alphanumeric characters and underscores. The value is limited to 32 KiB.
86
87The value can be either a file or a string value. For a file, its contents are saved to a temporary file on EAS Build servers. The file path is available via the environment variable. For example, if you created a file secret named `SECRET_FILE`, EAS Build will create a file at `/Users/expo/workingdir/environment-secrets/__UNIQUE_RANDOM_UUID__`, and `SECRET_FILE` will be set to that path.
88
89The secret values are encrypted at rest and in transit and are only decrypted in a secure environment by EAS servers.
90
91You can create up to 100 account-wide secrets for each Expo account and 100 app-specific secrets for each app. Account-wide secrets will be exposed to every build environment across all of your apps. App-specific secrets only apply to the app they're defined for and will override any account-wide secrets with the same name.
92
93You can manage secrets through the Expo website and EAS CLI.
94
95> **warning** Always remember that **anything that is included in your client side code should be considered public and readable to any individual that can run the application**.
96> EAS Secrets are intended to be used to provide values to an EAS Build job so that they may be used during the build process.
97> Examples of correct usage include setting the `NPM_TOKEN` for installing private packages from npm, or a Sentry API key to create a release and upload your sourcemaps to their service.
98> EAS Secrets do not provide any additional security for values that you end up embedding in your application itself, such as an AWS access key or other private keys.
99
100### Secrets on the Expo website
101
102To create **account-wide secrets**, navigate to [the "Secrets" tab in your account or organization settings](https://expo.dev/accounts/[account]/settings/secrets).
103
104To create **app-specific secrets**, navigate to [the "Secrets" tab in your project dashboard](https://expo.dev/accounts/[account]/projects/[project]/secrets). If you haven't published your project yet and it isn't visible on the website, you can create it on the website from this link.
105
106### Adding secrets with EAS CLI
107
108To create a new secret, run `eas secret:create`:
109
110<Terminal
111  cmd={[
112    '$ eas secret:create --scope project --name SECRET_NAME --value secretvalue --type string',
113    '✔ ️Created a new secret SECRET_NAME on project @fiberjw/goodweebs.',
114  ]}
115/>
116
117To view any existing secrets for this project, run `eas secret:list`:
118
119<Terminal
120  cmd={[
121    '$ eas secret:list',
122    'Secrets for this account and project:',
123    '┌────────────────┬────────┬─────────┬──────────────────────────────────────┬─────────────────┐',
124    '│ Name           │ Type   │ Scope   │ ID                                   │ Updated at      │',
125    '├────────────────┼────────┼─────────┼──────────────────────────────────────┼─────────────────┤',
126    '│ APP_UPLOAD_KEY │ string │ account │ 366bd434-b538-4192-887c-036c0eddedec │ Oct 05 11:51:46 │',
127    '├────────────────┼────────┼─────────┼──────────────────────────────────────┼─────────────────┤',
128    '│ NPM_TOKEN      │ string │ project │ 03f4881f-88fd-4d94-9e35-a5c34d39c2f2 │ Oct 05 11:51:33 │',
129    '├────────────────┼────────┼─────────┼──────────────────────────────────────┼─────────────────┤',
130    '│ SECRET_FILE    │ file   │ project │ 72c7ac1e-78d0-4fa2-b105-229260cecc88 │ Oct 05 11:52:12 │',
131    '├────────────────┼────────┼─────────┼──────────────────────────────────────┼─────────────────┤',
132    '│ sentryApiKey   │ string │ project │ 88dd0296-9119-4d50-a91b-1f646733f569 │ Oct 05 11:51:40 │',
133    '└────────────────┴────────┴─────────┴──────────────────────────────────────┴─────────────────┘',
134  ]}
135/>
136
137### Importing secrets from a dotenv file
138
139If you're using a **.env** file for storing your secrets locally, you can use the `eas secret:push` command to import all of them to EAS:
140
141<Terminal
142  cmd={[
143    '$ eas secret:push --scope project --env-file ./eas/.env',
144    '✔ Creating secrets on account johndoe...',
145    '✔ Created the following secrets on account johndoe:',
146    '- ABC',
147    '- DEF',
148    '- GHI',
149  ]}
150/>
151
152Beware that EAS CLI will fail if some of the secrets defined in the dotenv file already exist on the server. To force override those secrets, pass the `--force` flag to the command.
153
154#### Doppler integration
155
156You can use the `eas secret:push` command to integrate EAS with your [Doppler](https://doppler.com/) project:
157
158<Terminal
159  cmd={[
160    '$ doppler run --mount ./eas/.env -- eas secret:push --scope project --env-file ./eas/.env',
161  ]}
162/>
163
164### Accessing secrets in EAS Build
165
166After creating a secret, you can read it on subsequent EAS Build jobs with `process.env.VARIABLE_NAME` from Node.js or in shell scripts as `$VARIABLE_NAME`.
167
168## Common questions
169
170Environment variables can be tricky to use if you don't have the correct mental model for how they work. In this section, clarify common sources of confusion oriented around use cases are addressed below.
171
172### Can I share environment variables defined in eas.json with `expo start` and `eas update`?
173
174When you define environment variables on build profiles in **eas.json**, they will not be available for local development when you run `npx expo start`. A concern that developers often raise about this is that they now have to duplicate their configuration in multiple places, leading to additional maintenance effort and possible bugs when values go out of sync. If you find yourself in this situation, one possible solution is to move your configuration out of environment variables and into JavaScript. For example, imagine we had the following **eas.json**:
175
176```json eas.json
177{
178  "build": {
179    "production": {
180      "channel": "production",
181      "env": {
182        "API_URL": "https://api.production.com",
183        "ENABLE_HIDDEN_FEATURES": 0
184      }
185    },
186    "preview": {
187      "channel": "staging",
188      "env": {
189        "API_URL": "https://api.staging.com",
190        "ENABLE_HIDDEN_FEATURES": 1
191      }
192    }
193  }
194}
195```
196
197In **app.config.js**, we may be using the API URL like this:
198
199```js app.config.js
200export default {
201  // ...
202  extra: {
203    // Fall back to development URL when not set
204    apiUrl: process.env.API_URL ?? 'https://localhost:3000'
205    enableHiddenFeatures: process.env.ENABLE_HIDDEN_FEATURES ? Boolean(process.env.ENABLE_HIDDEN_FEATURES) : true,
206  }
207}
208```
209
210Using this approach, we would always need to remember to run `API_URL=https://api.staging.com ENABLE_HIDDEN_FEATURES=1 eas update` when updating staging, and something similar for production. If we forgot the `ENABLE_HIDDEN_FEATURES=0` flag when publishing to production, we might end up rolling out untested features to production, and if we forgot the `API_URL` value, then users would be pointed to `https://localhost:3000`!
211
212The following are two possible alternative approaches, each with different tradeoffs.
213
214<Step label="1">
215
216**Move values to application code and switch based on channel**. Rather than putting configuration in environment variables and extras, create a JavaScript file, possibly named **Config.js**. This approach will work well for you as long as you don't need to use the configuration values to modify build time configuration, such as the `ios.bundleIdentifier`, `icon`, and so on. This approach also gives you the ability to promote updates between environments, because the configuration that is used will switch when it's loaded from a binary with a different channel. It might look something like this:
217
218<Collapsible summary="Config.js">
219
220```js
221import * as Updates from 'expo-updates';
222
223let Config = {
224  apiUrl: 'https://localhost:3000',
225  enableHiddenFeatures: true,
226};
227
228if (Updates.channel === 'production') {
229  Config.apiUrl = 'https://api.production.com';
230  Config.enableHiddenFeatures = false;
231} else if (Updates.channel === 'staging') {
232  Config.apiUrl = 'https://api.staging.com';
233  Config.enableHiddenFeatures = true;
234}
235
236export default Config;
237```
238
239</Collapsible>
240
241</Step>
242
243<Step label="2">
244
245**Use a single environment variable to toggle configuration**. In our **eas.json** we can set an environment variable such as `APP_ENV` and then switch on that value inside of **app.config.js**. This way, we only have to be sure to set one environment variable: `APP_ENV=production eas update`.
246
247<Collapsible summary="eas.json">
248
249```json
250{
251  "build": {
252    "production": {
253      "channel": "production",
254      "env": {
255        "APP_ENV": "production"
256      }
257    },
258    "preview": {
259      "channel": "staging",
260      "env": {
261        "APP_ENV": "staging"
262      }
263    }
264  }
265}
266```
267
268</Collapsible>
269
270<Collapsible summary="app.config.js">
271
272```js
273let Config = {
274  apiUrl: 'https://localhost:3000',
275  enableHiddenFeatures: true,
276};
277
278if (process.env.APP_ENV === 'production') {
279  Config.apiUrl = 'https://api.production.com';
280  Config.enableHiddenFeatures = false;
281} else if (process.env.APP_ENV === 'staging') {
282  Config.apiUrl = 'https://api.staging.com';
283  Config.enableHiddenFeatures = true;
284}
285
286export default {
287  // ...
288  extra: {
289    ...Config,
290  },
291};
292```
293
294</Collapsible>
295
296</Step>
297
298### How are naming collisions between secrets and the `env` field in eas.json handled?
299
300A secret created on the Expo website or with `eas secret:create` will take precedence over an environment variable of the same name that is set through the `env` field in **eas.json**.
301
302For example, if you create a secret with the name `MY_TOKEN` and value `secret` and also set `"env": { "MY_TOKEN": "public" }` in your **eas.json**, then `process.env.MY_TOKEN` on EAS Build will evaluate to `secret`.
303
304### How do environment variables work for my Expo Development Client builds?
305
306Environment variables set in your build profile that impact **app.config.js** will be used for configuring the development build. When you run `npx expo start` to load your app inside of your development build, only environment variables that are available on your development machine will be used for the app manifest; this becomes the same situation as described above for **expo start**.
307
308### Can I just set my environment variables on a CI provider?
309
310Environment variables must be defined in **eas.json** to be made available to EAS Build builders. If you are [triggering builds from CI](/build/building-on-ci) this same rule applies, and you should be careful to not confuse setting environment variables on GitHub Actions (or the provider of your choice) with setting environment variables and secrets in **eas.json**.
311
312### How to upload a secret file and use it in my app config?
313
314A common use case for uploading file secrets to EAS is when you want to supply your build with the **google-services.json** and **GoogleService-Info.plist** files. Usually, those files should not be checked into the repository.
315
316Here's an example of how to upload **google-services.json** to EAS and use it in your app config:
317
318<Step label="1">
319
320Upload the file to EAS.
321
322<Terminal
323  cmd={[
324    '$ eas secret:create --scope project --name GOOGLE_SERVICES_JSON --type file --value ./path/to/google-services.json',
325    '✔ ️Created a new secret GOOGLE_SERVICES_JSON on project @user/myproject.',
326  ]}
327/>
328
329</Step>
330
331<Step label="2">
332
333Use **app.config.js** to read the path to **google-services.json**.
334
335```js app.config.js
336export default {
337  // ...
338  android: {
339    googleServicesFile: process.env.GOOGLE_SERVICES_JSON,
340    // ...
341  },
342};
343```
344
345</Step>
346