Using Docker Secrets with Vite: Solving the .env Issue

Using Docker secrets was more challenging than I expected. If you are struggling with an issue where the .envfile is undefined after setting up Dockerfile and docker-compose.yml, take a look at the setup that worked for me.

The key thing to understand is that in Vite, environment variables are loaded at build time, not at runtime. This means you can’t rely on .env files being accessible during execution as you would in a traditional backend setup.

Using Docker secrets is the recommended approach for handling sensitive data like passwords, API keys, and other credentials. It provides a more secure and manageable approach compared to using environment variables (ENV or ARG). However, keep in mind that Docker secrets only work in Swarm mode, so you’ll need to enable it before using them.

Step 1: Create the Dockerfile

The RUN step is where we mount the secrets into the build environment and write them to a .env file. This ensures that sensitive credentials are available during the build process without being hardcoded.

For example, here’s my .env

VITE_BASE_URL=https://server.com/api/v1
VITE_SOCKET_URL=https://server.com
VITE_GOOGLE_URL=https://server.com/api/v1/account/loginWithGoogle
# Use Node.js for building the Vite app
FROM node:18-alpine AS builder

# Set working directory
WORKDIR /app

# Copy package files first for better caching
COPY package.json package-lock.json ./
RUN npm install


# This script aggregates various secrets into a .env file, which is then used by Vite during the build process to inject environment variables into the application.
RUN --mount=type=secret,id=base_url \
    --mount=type=secret,id=socket_url \
    --mount=type=secret,id=google_url \
    set -e; \
    echo "VITE_BASE_URL=$(cat /run/secrets/base_url)" >> .env && \
    echo "VITE_SOCKET_URL=$(cat /run/secrets/socket_url)" >> .env && \
    echo "VITE_GOOGLE_URL=$(cat /run/secrets/google_url)" >> .env && \

# Copy project files after .env has been created
COPY . .

#This step is optional. Outputs the contents of the .env file to confirm correct environment variable setup.
RUN node -e "console.log(require('fs').readFileSync('.env', 'utf8'))"

# Build the Vite app
RUN npm run build

# Install a simple HTTP server for serving static content
RUN npm install -g serve

# Expose the port the app runs on
EXPOSE 5173

# Serve the app
CMD ["serve", "-s", "dist", "-l", "5173"]

Step 2: Create the docker-compose.yml

version: "3.8"

services:
  vite-app:
    build: .
    ports:
      - "5173:5173"
    secrets:
      - base_url
      - socket_url
      - google_url
    environment:
      VITE_BASE_URL_FILE: /run/secrets/base_url
      VITE_SOCKET_URL_FILE: /run/secrets/socket_url
      VITE_GOOGLE_URL_FILE: /run/secrets/google_url

secrets:
  base_url:
    external: true
  socket_url:
    external: true
  google_url:
    external: true

Step 3: In your terminal, run this:

docker swarm init

Step 4: Create Docker secret

echo "my-secret-value" | docker secret create base_url -
echo "my-secret-value" | docker secret create socket_url -
echo "my-secret-value" | docker secret create google_url -

Step 5: Build image

DOCKER_BUILDKIT=1 docker build \
  --no-cache \
  --pull \
  --secret id=base_url,src=secrets/base_url.txt \
  --secret id=socket_url,src=secrets/socket_url.txt \
  --secret id=google_url,src=secrets/google_url.txt \
  -t my-image-name .

Step 6: Tag the image and push to Dockerhub

docker tag my-image-name dockerhub_username/my-image-name
docker push dockerhub_username/my-image-name

Since I already built the image, I can run a service with a docker-compose.yml

docker stack deploy -c docker-compose.yml my_1_stack

It took me a while to get this working with Vite, but this approach finally solved my issue. If you’re running into problems or have any questions, feel free to ask!

Happy coding!

Published by

Leave a comment