Fly.io Secrets Not Automatically Injected into Docker Build

Hello Fly.io community,

I’m having an issue where environment variables from Fly.io secrets are not automatically passed into the Docker build during deployment. I currently need to manually pass the secrets using the --build-arg flag like this:

fly deploy --build-arg VITE_APP_API_ENDPOINT=$VITE_APP_API_ENDPOINT --verbose --depot=false

While this works, can we expected Fly.io to automatically inject the secrets during the build, without needing to manually specify --build-arg.

Current Behavior:

  • Manual --build-arg Usage: I need to manually specify the values from Fly.io secrets using --build-arg, which isn’t ideal when managing sensitive data. This defeats the purpose of using Fly.io secrets for secure storage.

the Dockerfile


FROM node:20.9.0-slim AS base

LABEL fly_launch_runtime="Node.js"
WORKDIR /app

ARG VITE_FIREBASE_API_KEY
ARG VITE_AUTH_DOMAIN
ARG VITE_PROJECT_ID
ARG VITE_STORAGE_BUCKET
ARG VITE_MESSAGING_SENDER_ID
ARG VITE_APP_ID
ARG VITE_NODE_ENV=production
ARG VITE_APP_API_ENDPOINT


ENV VITE_FIREBASE_API_KEY=${VITE_FIREBASE_API_KEY}
ENV VITE_AUTH_DOMAIN=${VITE_AUTH_DOMAIN}
ENV VITE_PROJECT_ID=${VITE_PROJECT_ID}
ENV VITE_STORAGE_BUCKET=${VITE_STORAGE_BUCKET}
ENV VITE_MESSAGING_SENDER_ID=${VITE_MESSAGING_SENDER_ID}
ENV VITE_APP_ID=${VITE_APP_ID}
ENV VITE_NODE_ENV=${VITE_NODE_ENV}
ENV VITE_APP_API_ENDPOINT=${VITE_APP_API_ENDPOINT}
# Set production environment
ENV NODE_ENV="production"
ENV VITE_APP_NODE_ENV="production"


# Throw-away build stage to reduce size of final image
FROM base AS build


ARG VITE_FIREBASE_API_KEY
ARG VITE_AUTH_DOMAIN
ARG VITE_PROJECT_ID
ARG VITE_STORAGE_BUCKET
ARG VITE_MESSAGING_SENDER_ID
ARG VITE_APP_ID
ARG VITE_NODE_ENV=production
ARG VITE_APP_API_ENDPOINT

ENV VITE_FIREBASE_API_KEY=${VITE_FIREBASE_API_KEY}
ENV VITE_AUTH_DOMAIN=${VITE_AUTH_DOMAIN}
ENV VITE_PROJECT_ID=${VITE_PROJECT_ID}
ENV VITE_STORAGE_BUCKET=${VITE_STORAGE_BUCKET}
ENV VITE_MESSAGING_SENDER_ID=${VITE_MESSAGING_SENDER_ID}
ENV VITE_APP_ID=${VITE_APP_ID}
ENV VITE_NODE_ENV=${VITE_NODE_ENV}
ENV VITE_APP_API_ENDPOINT=${VITE_APP_API_ENDPOINT}

ENV NODE_ENV="production"
ENV VITE_APP_NODE_ENV="production"

# Debugging Step: Check if environment variables are passed
RUN echo "-- 1 -- Building with VITE_APP_API_ENDPOINT=${VITE_APP_API_ENDPOINT}"

# Validate that VITE_APP_API_ENDPOINT is not empty
RUN if [ -z "$VITE_APP_API_ENDPOINT" ]; then \
      echo "Error: VITE_APP_API_ENDPOINT is not set or is empty"; \
      exit 1; \
    fi


    # Install packages needed to build node modules
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3

# Install dependencies
COPY package-lock.json package.json ./
# **Ensure devDependencies are installed (Required for Vite)**
RUN npm ci --include=dev

# Install Vite globally (as a backup)
RUN npm install -g vite

# **Debugging Step: Check if Vite is installed**
RUN ls -la node_modules | grep vite || echo "Vite is missing!"
# Fail if Vite is missing
RUN ls -la node_modules | grep vite || { echo "Error: Vite is missing!"; exit 1; }

COPY . .
RUN npm run build-prod

# Final stage for app image
FROM base
WORKDIR /app

# Install serve for static hosting
RUN npm install -g serve
COPY --from=build /app/dist ./dist

# Expose port and serve static files
EXPOSE 3000
CMD ["serve", "-s", "dist", "-l", "3000"]

Fly.io Secrets Confirmation:

 fly secrets list
NAME                            DIGEST                  CREATED AT
REACT_APP_API_URL               0847baf66c9d7b7a        2h9m ago
VITE_APP_API_ENDPOINT           906b815ac837784a        Mar 14 2025 18:57
VITE_APP_ID                     8e66c6425811921f        Mar 14 2025 18:57
VITE_AUTH_DOMAIN                3b55d2019b5d2cde        Mar 14 2025 18:57
VITE_FIREBASE_API_KEY           1f2f62a799b37185        Mar 14 2025 18:57
VITE_MESSAGING_SENDER_ID        fabe0bd727dda1c6        Mar 14 2025 18:57
VITE_NODE_ENV                   a331102148f18977        Mar 14 2025 18:57
VITE_PROJECT_ID                 3cfecda489f06ea5        Mar 14 2025 18:57
VITE_STORAGE_BUCKET             faedfad2e6b90447        Mar 14 2025 18:57

Question:

Is there a way to automatically inject Fly.io secrets into the Docker build without manually specifying --build-arg?

Thanks for any help!

I wouldn’t use --build-arg, I assume that stores them in your image, which isn’t ideal for secrets.

For non-secret secrets, I’d use flyctl deploy --env, and for secret secrets, I’d use flyctl secrets, which presumably encrypts/decrypts a stored value and exposes it as an env var.

Ah, sorry, I should have read more closely; you’re aware of secrets. OK, can you explain why you want to do this at build time? Doing it at run-time really is the preferred way to go.

well, since its a react app, all env are being hard coded in build time to the static files in dist - so there is no run time.

Not necessarily. How are you building your app? Are you using Vite? Are you accessing environment variables in your source files with import.meta.env? This approach requires the environment variables during the build process.

In our case, we use Remix and React Router, but conceptually, that doesn’t matter. We have a “resource route” that returns the safe client environment variables as JSON. The client requests this at runtime, which sets some “global variables” on window.