NeuroAgent

How to Use Variables in Dockerfile ENTRYPOINT JSON Format

Solution for substituting variables in Dockerfile ENTRYPOINT JSON format without additional layers and symbolic links.

Question

How can I use variables in Dockerfile ENTRYPOINT in JSON format?

Hello! I have several applications that are built using the same Dockerfile structure, but they only differ in the compilation directory name and the executable file name. This forces me to use multiple Dockerfiles.

I want to create a single parameterized template Dockerfile. I don’t have issues with directory names, but I’m running into difficulties with the ENTRYPOINT layer.

If ENTRYPOINT is described in JSON (exec) format, the variable doesn’t get substituted:

ENTRYPOINT ["dotnet", "${APPLICATION}.dll"]

If I use shell form, parameterization works:

ENTRYPOINT ["sh","-c","exec dotnet ${APPLICATION}.dll"]

But what should I do if it’s crucial that ENTRYPOINT remains in JSON format?

I found a temporary solution through creating a symbolic link, but it requires an additional layer and doesn’t look very elegant:

RUN ln -s "${APPLICATION}.dll" /app/app.dll
ENTRYPOINT ["dotnet","/app/app.dll"]

Here’s an example of my Dockerfile (APP is the service name):

dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
WORKDIR /app
ENV DOTNET_NUGET_SIGNATURE_VERIFICATION=false
COPY . .
RUN cd __APP__ && dotnet publish -c Release -o publish /p:DebugType=None /p:DebugSymbols=false

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build-env /app/__APP__/publish .
ENTRYPOINT ["dotnet", "__APP__.dll"]

How can I properly use variables in ENTRYPOINT Dockerfile in JSON format without creating additional layers and symbolic links?

NeuroAgent

Docker ENTRYPOINT JSON Format Doesn’t Support Variable Substitution at Build Time

The Docker ENTRYPOINT in JSON format doesn’t support variable substitution at build time because this form executes the command directly through exec() rather than through a shell where variable processing occurs. However, there are several elegant solutions for parameterizing ENTRYPOINT without creating additional layers.


Table of Contents


The Main Problem with JSON ENTRYPOINT Format

As explained in the Docker documentation, when ENTRYPOINT is specified in JSON format (exec form), the command is executed directly through exec() rather than through a shell. This means environment variables are not substituted at build time:

dockerfile
# ❌ Doesn't work - variable is not substituted
ENTRYPOINT ["dotnet", "${APPLICATION}.dll"]

Unlike the shell form, where variables are processed:

dockerfile
# ✅ Works, but not JSON format
ENTRYPOINT ["sh","-c","exec dotnet ${APPLICATION}.dll"]

According to Joe Yates’ blog, this is an important limitation of the JSON format that often causes confusion.


Solution Using Templating

The cleanest approach is to use templating at build time. Create an ENTRYPOINT template and substitute variables during the build process:

Step 1: Create an entrypoint.template file

bash
#!/bin/sh
exec dotnet ${APPLICATION}.dll

Step 2: In Dockerfile, use the template

dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
WORKDIR /app
ENV DOTNET_NUGET_SIGNATURE_VERIFICATION=false
COPY . .
RUN cd __APP__ && dotnet publish -c Release -o publish /p:DebugType=None /p:DebugSymbols=false

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build-env /app/__APP__/publish .

# Create a template with variables
RUN echo "#!/bin/sh\nexec dotnet \${APPLICATION}.dll" > /entrypoint.template

# Substitute variables and create an executable script
RUN sed -i "s/__APP__/${APPLICATION}/g" /entrypoint.template && \
    chmod +x /entrypoint.template

# Use the script in ENTRYPOINT JSON format
ENTRYPOINT ["/entrypoint.template"]

This approach allows you to maintain the JSON format of ENTRYPOINT and avoid symbolic links.


Solution Through a Startup Script

Create a universal startup script that can work with different applications:

Startup script start.sh:

bash
#!/bin/sh
# Define the application name from the variable or default
APP_NAME=${APPLICATION:-app}

# Start the application
exec dotnet "${APP_NAME}.dll"

Dockerfile:

dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
WORKDIR /app
ENV DOTNET_NUGET_SIGNATURE_VERIFICATION=false
COPY . .
RUN cd __APP__ && dotnet publish -c Release -o publish /p:DebugType=None /p:DebugSymbols=false

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build-env /app/__APP__/publish .
COPY start.sh /app/

# Set the application variable
ARG APPLICATION=app
ENV APPLICATION=${APPLICATION}

# Make the script executable and use it
RUN chmod +x /app/start.sh
ENTRYPOINT ["/app/start.sh"]

This approach is very flexible and allows you to easily add new startup logic.


Solution Using ARG Variables

Although ARG variables are not directly substituted into ENTRYPOINT JSON, you can use them to create a dynamic Dockerfile:

dockerfile
ARG APPLICATION=app

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
WORKDIR /app
ENV DOTNET_NUGET_SIGNATURE_VERIFICATION=false
COPY . .
RUN cd ${APPLICATION} && dotnet publish -c Release -o publish /p:DebugType=None /p:DebugSymbols=false

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build-env /app/${APPLICATION}/publish .

# Create a file with the correct application name
RUN echo "${APPLICATION}.dll" > /app/appname.txt

# Use a script to read the filename
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh:

bash
#!/bin/sh
APP_NAME=$(cat /app/appname.txt)
exec dotnet "${APP_NAME}"

Solution Using Build Tools

Use build tools to generate the Dockerfile:

Using a Makefile:

makefile
build:
	@sed "s/__APP__/$(APPLICATION)/g" Dockerfile.template > Dockerfile
	docker build -t myapp:$(APPLICATION) .

run:
	docker run -e APPLICATION=$(APPLICATION) myapp:$(APPLICATION)

Dockerfile.template:

dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
WORKDIR /app
ENV DOTNET_NUGET_SIGNATURE_VERIFICATION=false
COPY . .
RUN cd __APP__ && dotnet publish -c Release -o publish /p:DebugType=None /p:DebugSymbols=false

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build-env /app/__APP__/publish .
ENTRYPOINT ["dotnet", "__APP__.dll"]

Comparison of Approaches

Approach Advantages Disadvantages
Templating Clean solution, maintains JSON format Requires additional build steps
Startup Script Flexible, easily extensible May add slight overhead
ARG + file Uses standard Docker mechanisms Requires creating an additional file
Build Tools Maximum flexibility Requires external tools

Recommended Approach

For most cases, I recommend the startup script solution, because it:

  1. Maintains the JSON format of ENTRYPOINT
  2. Doesn’t require additional Docker layers
  3. Is easy to maintain and extend
  4. Works with any environment variables
  5. Allows adding error handling, logging, etc.

Example final Dockerfile:

dockerfile
ARG APPLICATION=app

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
WORKDIR /app
ENV DOTNET_NUGET_SIGNATURE_VERIFICATION=false
COPY . .
RUN cd ${APPLICATION} && dotnet publish -c Release -o publish /p:DebugType=None /p:DebugSymbols=false

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build-env /app/${APPLICATION}/publish .
COPY start.sh /app/

# Set the application variable
ENV APPLICATION=${APPLICATION}

# Make the script executable and use it
RUN chmod +x /app/start.sh
ENTRYPOINT ["/app/start.sh"]

start.sh:

bash
#!/bin/sh
# Process environment variables
APP_NAME=${APPLICATION:-app}

# Add logging
echo "Starting application: $APP_NAME"

# Start the application
exec dotnet "${APP_NAME}.dll"

This approach solves your problem elegantly without creating additional layers or symbolic links.


Sources

  1. Dockerfile reference | Docker Docs
  2. Docker ENTRYPOINT, CMD and run Arguments - Joe Yates’ Blog
  3. Integrating Docker Environment Variables in ENTRYPOINT Array | Baeldung on Ops
  4. How do I use Docker environment variable in ENTRYPOINT array? - Stack Overflow
  5. JSONArgsRecommended | Docker Docs
  6. Docker ARG, ENV and .env - a Complete Guide · vsupalov.com