---
title: Astro
description: How to integrate varlock with Astro for secure, type-safe environment management
---
import SyncedCodeTabs from '@/components/SyncedCodeTabs.astro'
import { TabItem, Tabs, Steps } from "@astrojs/starlight/components";
import Badge from '@/components/Badge.astro';
import ExecCommandWidget from '@/components/ExecCommandWidget.astro';
import InstallJsDepsWidget from '@/components/InstallJsDepsWidget.astro';

<div class="page-badges">
  <Badge npmPackage="@varlock/astro-integration" />
</div>

While Astro has [`astro:env`](https://docs.astro.build/en/guides/environment-variables/) to help with environment variables, we think Varlock has more to offer:

- Your `.env.schema` is not tied to JavaScript, and is a better place to store this schema info versus your `astro.config.*` file
- Facilitates loading and composing multiple `.env` files
- You can use validated env vars right away within your `astro.config.*` file
- Facilitates setting values and handling multiple environments, not just setting defaults
- More data types and options available
- Leak detection, log redaction, and more security guardrails

To integrate varlock into an Astro application, you must use our [`@varlock/astro-integration`](https://npmx.dev/package/@varlock/astro-integration) package, which is an [Astro integration](https://docs.astro.build/en/guides/integrations-guide/).

## Setup

:::caution[Requirements]
- Node.js v22 or higher
- Astro v4 or higher
:::
{/* check astro versions compat? */}

<Steps>

1. **Install varlock and the Astro integration package**
    <InstallJsDepsWidget packages="@varlock/astro-integration varlock" />

1. **Run `varlock init` to set up your `.env.schema` file**

    This will guide you through setting up your `.env.schema` file, based on your existing `.env` file(s). Make sure to review it carefully.

    <ExecCommandWidget command="varlock init" showBinary={false} />

1. **Enable the Astro integration**

    You must add our `varlockAstroIntegration` to your `astro.config.*` file:

    ```diff lang="ts" title="astro.config.ts" add="varlockAstroIntegration()"
    import { defineConfig } from 'astro/config';
    +import varlockAstroIntegration from '@varlock/astro-integration';

    export default defineConfig({
      integrations: [varlockAstroIntegration(), otherIntegration()],
    });
    ```
</Steps>

---

## Accessing environment variables

You can continue to use `import.meta.env.SOMEVAR` as usual, but we recommend using varlock's imported `ENV` object for better type-safety and improved developer experience:

```ts title="example.ts"
import { ENV } from 'varlock/env';

console.log(import.meta.env.SOMEVAR); // 🆗 still works
console.log(ENV.SOMEVAR);             // ✨ recommended
```

#### Why use `ENV` instead of `import.meta.env`?
- Non-string values (e.g., number, boolean) are properly typed and coerced
- All non-sensitive items are replaced at build time (not just `VITE_` prefixed ones)
- Better error messages for invalid or unavailable keys
- Enables future DX improvements and tighter control over what is bundled

### Within `astro.config.*`
It's often useful to be able to access env vars in your Astro config. Without varlock, it's a bit awkward, but varlock makes it easy. In fact it's already available: import varlock's `ENV` object and reference env vars via `ENV.SOME_ITEM` like you do everywhere else.

```diff lang="ts" title="astro.config.ts"
import { defineConfig } from 'astro/config';
import varlockAstroIntegration from '@varlock/astro-integration';
+import { ENV } from 'varlock/env';

+doSomethingWithEnvVar(ENV.FOO);

export default defineConfig({ /* ... */ });
```

:::caution[TypeScript config]
If you find you are not getting type completion on `ENV`, you may need to add your generated type files (usually `env.d.ts`) to your `tsconfig.json`'s `include` array.
:::

### Within other scripts

Even in a static front-end project, you may have other scripts in your project that rely on sensitive config.

You can use [`varlock run`](/reference/cli-commands/#run) to inject resolved config into other scripts as regular env vars.

<ExecCommandWidget command="varlock run -- node ./script.js" showBinary={false} />

### Type-safety and IntelliSense

To enable type-safety and IntelliSense for your env vars, enable the [`@generateTsTypes` root decorator](/reference/root-decorators/#generatetstypes) in your `.env.schema`. Note that if your schema was created using `varlock init`, it will include this by default.

```diff lang="env-spec" title=".env.schema"
+# @generateTsTypes(path='env.d.ts')
# ---
# your config items...
```

---

## Managing multiple environments

Varlock can load multiple _environment-specific_ `.env` files (e.g., `.env.development`, `.env.preview`, `.env.production`) by using the [`@currentEnv` root decorator](/reference/root-decorators/#currentenv). **This is different than Astro/Vite's default behaviour, which relies on it's own [`MODE` flag](https://vite.dev/guide/env-and-mode.html#modes).**

Usually this env var will be defaulted to something like `development` in your `.env.schema` file, and you can override it by overriding the value when running commands - for example `APP_ENV=production vite build`. For a JavaScript based project, this will often be done in your `package.json` scripts.

```diff lang="json" title="package.json" add=/APP_ENV=[a-z]+/
{
  "scripts": {
    "dev": "astro dev",
    "build": "APP_ENV=production astro build",
    "preview": "APP_ENV=production vite preview",
  }
}
```

In some cases, you could also set the current environment value based on other vars already injected by your CI platform, like the current branch name. See the [environments guide](/guides/environments) for more information.

## Managing sensitive config values

Astro uses the `PUBLIC_` prefix to determine which env vars are public (bundled for the browser). Varlock decouples the concept of being _sensitive_ from key names, and instead you control this with the [`@defaultSensitive`](/reference/root-decorators/#defaultsensitive) root decorator and the [`@sensitive`](/reference/item-decorators/#sensitive) item decorator. See the [secrets guide](/guides/secrets) for more information.

Set a default and explicitly mark items:

```diff lang="env-spec" title=".env.schema" add="@sensitive"
+# @defaultSensitive=false
# ---
NON_SECRET_FOO= # sensitive by default
# @sensitive
SECRET_FOO=
```

Or if you'd like to continue using Astro's prefix behavior:

```diff lang="env-spec" title=".env.schema"
+# @defaultSensitive=inferFromPrefix('PUBLIC_')
# ---
FOO= # sensitive
PUBLIC_FOO= # non-sensitive, due to prefix
```

:::caution[Bundling behavior]
All non-sensitive items are bundled at build time via `ENV`, while `import.meta.env` replacements continue to only include `PUBLIC_`-prefixed items.
:::

### Leak Detection

This integration will automatically inject a new middleware that scans outgoing http responses for any sensitive values.



:::note[Encrypting the env blob]
When deploying SSR Astro apps to serverless platforms, varlock injects the resolved env into your server-side build output as plaintext JSON. This is generally safe since it only appears in server code, but you can encrypt it for extra protection (e.g., against sourcemap leaks). See the [encrypted deployments guide](/guides/encrypted-deployments/) for setup instructions.
:::

---

## Deploying to Cloudflare Workers

If you deploy your SSR Astro app to Cloudflare Workers via [`@astrojs/cloudflare`](https://docs.astro.build/en/guides/integrations-guide/cloudflare/), install [`@varlock/cloudflare-integration`](/integrations/cloudflare/) alongside the Astro integration. The Astro integration **auto-detects** the Cloudflare adapter and wires up env injection into the worker. There's no extra Vite or adapter config to write, and you keep using `astro dev` as normal.

<Steps>

1. **Install the Cloudflare integration**

    <InstallJsDepsWidget packages="@varlock/cloudflare-integration" />

1. **Use the Cloudflare adapter as usual**

    No varlock-specific Cloudflare setup is needed. Use the standard adapter alongside `varlockAstroIntegration()`:

    ```ts title="astro.config.ts"
    import { defineConfig } from 'astro/config';
    import cloudflare from '@astrojs/cloudflare';
    import varlockAstroIntegration from '@varlock/astro-integration';

    export default defineConfig({
      output: 'server',
      adapter: cloudflare(),
      integrations: [varlockAstroIntegration()],
    });
    ```

1. **Deploy with `varlock-wrangler`**

    Use [`varlock-wrangler`](/integrations/cloudflare/#varlock-wrangler-cli) so your resolved env is uploaded as Cloudflare vars and secrets at deploy time:

    ```diff lang="json" title="package.json" add="varlock-"
    {
      "scripts": {
        "dev": "astro dev",
        "build": "astro build",
        "deploy": "astro build && varlock-wrangler deploy"
      }
    }
    ```

</Steps>

:::caution[Requires `nodejs_compat`]
Varlock relies on a handful of `node:*` built-ins at runtime, so your worker must enable Cloudflare's Node.js compatibility layer. Add `nodejs_compat` to `compatibility_flags` in your `wrangler.json` / `wrangler.toml`.
:::

:::caution[Remove `.dev.vars`]
Varlock manages env injection automatically. Delete any `.dev.vars` file in your project root, or the dev server will error.
:::

See the [Cloudflare Workers integration docs](/integrations/cloudflare/) for full details on how dev injection, deployment, CI/CD, and `varlock-wrangler` work.

---

## Reference
- [Root decorators reference](/reference/root-decorators)
- [Item decorators reference](/reference/item-decorators)
- [Functions reference](/reference/functions)
- [Astro's environment variable docs](https://docs.astro.build/en/guides/environment-variables/)
