Azure Functions and Docker
Azure Functions v3 in Docker like a Pro
What I’ve learnt from dockerizing Azure Functions

Since I started writing and deploying Azure Functions a lot have changed. In this article we will create a linux Docker container for an Azure Function v3.
But before a few questions from the skeptical ones:
Why Docker?
Because you can deploy in a robust, comfortable, smart way. 🐳
For example… You put your images into an Azure Container Registry. Then you one-click-deploy an ARM template (with dockerized resources) and boom! You have all the resources up and running.
Why Azure Function Version 3?
Because it’s the new Azure standard for Azure Functions. They work great! Why not migrate now? ⚡
Why Linux?
Beacause a Linux Azure App Service Plan is cheaper! 🐧
Why Azure Functions?
Seriously? Needless to say, they are awesome! 🧡
TLDR: We want the c0de! Feel free to skip all the introduction and go to the last paragraph.
First Naive Approach
I love coding in Visual Studio, and I created all my Azure Functions there. A lot of people prefer VS Code with its internal console. It’s great.
But I’m an uncurable lazy dev. And I love the right-click-publish feature…
That’s why the naive approach was simply to right click the function project and select “Add Docker Support”.
The Docker Nuget was installed this way and a brand-new dockerfile was created.
Great! Article ended! - “Not so quick, Potter…”
Let’s build and publish!

Build problem:
Step 4/18 : FROM mcr.microsoft.com/dotnet/core/sdk:3.1-stretch AS build
manifest for mcr.microsoft.com/dotnet/core/sdk:3.1-stretch not found: manifest unknown
Docker command failed with exit code 1.
Ok, a little bug I can fix manually. Let’s use another tag from the dotnet core dockerhub page.
Replace mcr.microsoft.com/dotnet/core/sdk:3.1-stretch
with mcr.microsot.com/dotnet/core/sdk:3.1
.
This time we successfully build, tag and push the function and we get no errors in console. 😎

- Publish problem 1: I create a Function in the Azure portal. Then I deploy this container and bind it to the function. In the function blade I can’t see any trace of it!
- Publish problem 2: My Docker container image is called lftestdockerizingfunctionsfunction:latest. Argh.
Problem 2 is simple
Tag name is taken from the namespace of our function. Then it’s rewritten in lower case and without points. It’s not readable and out of standard.
Let’s add in the .csproj
file the following line:
<DockerfileTag>lf.test.dockerizingfunctions.function</DockerfileTag>
Now let’s face problem 1
We already check that every step is correct, we haven’t got any error publishing and we can’t see any function running in the Azure Portal.
Let’s try debugging with Kudu the status of our Azure Function.
In SSH we get a connection error. That’s a shame.

In Bash we can navigate the App Service filesystem. But it’s useless since our function runs in a Docker container.
In the log files we can see that Docker starts our function. But in the function blade not a whiff of our function.
docker run -d -p 3497:8081
--name dockerizingfunctions_1_aacaadc6_middleware
-e WEBSITE_CORS_ALLOWED_ORIGINS =
https://functions.azure.com,
https://functions-staging.azure.com,
https://functions-next.azure.com
-e WEBSITE_CORS_SUPPORT_CREDENTIALS=False
-e WEBSITES_ENABLE_APP_SERVICE_STORAGE=false
-e WEBSITE_SITE_NAME=dockerizingfunctions
-e WEBSITE_AUTH_ENABLED=False
-e WEBSITE_ROLE_INSTANCE_ID=0
-e WEBSITE_HOSTNAME = dockerizingfunctions.azurewebsites.net
-e WEBSITE_INSTANCE_ID = e3a8761ce3056497006ce92f995ddcf6bb4651f4a55edeef03442174d6d35b37 appsvc/middleware:2001061754
/Host.ListenUrl = http://0.0.0.0:8081
/Host.DestinationHostUrl = http://172.16.0.6:80
/Host.UseFileLogging = true
Ok, take a breath and erase all the dockerfile content. 😅
It’s simply wrong.
Second Approach
I start from scratch and this time I use the Azure Functions Core Tools. I create another empty function using the command func init --docker-only --worker-runtime dotnet
.
Now we have a completely different and working dockerfile! I get back to Visual Studio and paste this new dockerfile.
We only need to change a few things. First of all replace sdk:3.0
with sdk:3.1
and the other thing that differs is the build context.
- The Core Tools dockerfile is executed in the same directory of the csproj, so all the files are copied with the command
COPY . <destination-path>
. - In Visual Studio, the build context is the directory where the
.sln
file is, not the.csproj
. So we have to enter a directory and copy all the function files.
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS installer-env
COPY ["LF.Test.DockerizingFunctions.Function/.", "/src/dotnet-function-app"]
RUN cd /src/dotnet-function-app && \
mkdir -p /home/site/wwwroot && \
dotnet publish *.csproj --output /home/site/wwwrootFROM mcr.microsoft.com/azure-functions/dotnet:3.0
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
AzureFunctionsJobHost__Logging__Console__IsEnabled=true
COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"]
SSH Connection error
A nice feature to add in our function is SSH support. This could be useful to debug and test.
Let’s change the image mcr.microsoft.com/azure-functions/dotnet:3.0
in the second block of the dockerfile with the image mcr-microsoft.com/azure-functions/dotnet:3.0-appservice
.

Some mistakes I did that you will avoid
- If you’re creating a function with ARM template like me, always put some magic words in function app config! Be sure that you have
WEBSITES_ENABLE_APP_SERVICE_STORAGE
set tofalse
or you won’t see your function! - Upgrading is always more painful than writing new code. Remember to set the environment variable
FUNCTIONS_EXTENSION_VERSION
to~3
. - Be aware of the path in dockerfile. Sometimes you have to copy some references before publishing the app.
- If you need to use DevOps artifacts you should split the
dotnet publish
into adotnet restore --configfile NuGet.Config
and adotnet build
. - Debugging containers isn’t simple at all. You could use SSH for production containers. In a local environment you could use
docker exec -it <container> /bin/sh
to open a shell inside the container.
Brief Recap
We had a few problems with the Visual Studio generated dockerfile. We fixed them re-writing it completely.
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS installer-env
COPY ["LF.Test.DockerizingFunctions.Function/.", "/src/dotnet-function-app"]
RUN cd /src/dotnet-function-app && \
mkdir -p /home/site/wwwroot && \
dotnet publish *.csproj --output /home/site/wwwrootFROM mcr.microsoft.com/azure-functions/dotnet:3.0-appservice
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
AzureFunctionsJobHost__Logging__Console__IsEnabled=true
COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"]
And that’s it… Thanks for reading!