# CI/CD

Pipelines authenticate with a **service token**. Create one under **Project → Tokens**:

- Pick the environments it may read.
- Keep it read-only unless the job writes values.
- Choose an expiry: 30, 90 or 365 days.

The token is shown once. Store it in your CI's secret store as `ENVPASS_TOKEN`. A token stops working when it expires, when it is revoked, or when the person who created it leaves the workspace.

A token reads the team values of its project. It never receives anyone's personal overrides. If a repository deploys several apps with different values, create a project and a token for each.

## GitHub Actions

```yaml
jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      ENVPASS_TOKEN: ${{ secrets.ENVPASS_TOKEN }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npx envpass-cli pull -e prod -o .env
      - run: npm run build
```

Or inject values for a single step without writing a file:

```yaml
      - run: npx envpass-cli run -e prod -- npm test
```

To skip the token, [sync](/docs/syncs) the environment to the repository's Actions secrets or to a GitHub environment, and read them with `${{ secrets.NAME }}` as usual.

## Vercel, Netlify and other build platforms

On Vercel, a [sync](/docs/syncs) is the simplest option: envpass writes the values to the Vercel project's environment variables after every change, and the next deployment uses them.

On any platform, you can instead add `ENVPASS_TOKEN` to the project's environment variables and pull before the build:

```json
{
  "scripts": {
    "build": "envpass-cli pull -e prod -o .env.production && next build"
  },
  "devDependencies": {
    "envpass-cli": "latest"
  }
}
```

## Docker

Pass the token at runtime and let the container resolve values on start:

```dockerfile
FROM node:22-slim
WORKDIR /app
COPY . .
RUN npm ci && npm install -g envpass-cli
CMD ["envpass", "run", "-e", "prod", "--", "node", "server.js"]
```

```bash
docker run -e ENVPASS_TOKEN=ep_live_... my-image
```

Avoid baking secrets into image layers. Resolve them when the container starts.

## Python apps

```bash
uvx envpass-cli run -e prod -- gunicorn app:app
```

## Plain HTTP

Any tool that can make an HTTP request can read values:

```bash
curl -fsS -H "Authorization: Bearer $ENVPASS_TOKEN" \
  "https://envpass.dev/api/v1/resolve?environment=prod&format=dotenv" > .env
```

`format` can be `dotenv`, `shell` (`export KEY='value'` lines for `eval`) or `json`.
