Github Actions
Github Actions is a new workflow automation tool for building, testing, and deploying your code directly from Github.
If you’re already using Github for your code repository, there’s no need to integrate a third-party tool to automate common developer workflows.
Simply place a Github Actions .yml
file in your repository, designed to run in response to specific events. (push, pull requests, etc)
Workflow diagram
Today we’re going to go through an example workflow using Python to build a barebones API, with FastAPI as the framework of choice.

We will build and push a container image to Azure Container Registry, and pull the image into an Azure Web App for deployment.
Note that this article assumes prior knowledge of the technologies listed below.
The shell command examples given are on macOS. But you should be able to find equivalent commands if you’re developing on Windows.
The full list of packages and technologies we’ll be utilizing include:
- VS Code – as the IDE of choice.
- pipenv – to handle package dependencies, create virtual environments, and load environment variables.
- FastAPI— Python API development framework.
- Uvicorn – ASGI server for FastAPI app.
- Docker Desktop— build and run Docker container images on our local machine.
- Azure Container Registry – repository for storing our container image in Azure cloud.
- Azure App Service – PaaS service to host our Fastapi app server.
- Github Actions – automate continuous deployment workflow of FastAPI app.
Go ahead and download VS Code and Docker Desktop from the links above, as we will be needing those two programs throughout this guide.
Project setup on local machine
Create your project folder
In your terminal, navigate to a folder where you want to create your project, and run the following commands.
mkdir fastapi-cd
cd fastapi-cd
code .

VS Code should start up with the project folder you created.
Install pipenv
pipenv is a versatile package that removes the need for a requirements.txt
file usually found in Python apps.
Hit Ctrl + ~
to open a VS Code terminal, and enter the commands:
sudo -H pip install -U pipenv
pipenv install fastapi uvicorn

pipenv will proceed to install the libraries in a virtual environment and manage the dependencies for you in a Pipfile
.

Because pipenv creates a virtual environment for your project, you don’t need to use the regular venv / virtualenv
command either.
Simply use pipenv shell
to activate the virtual environment.
pipenv shell
uvicorn --version
exit
uvicorn --version
Use the exit
command to exit the virtual environment.
Note that uvicorn is no longer accessible outside of the environment. It was only installed in the virtual environment accessible using pipenv shell
.

Create project files
Create the following files in the project directory.
touch main.py .env .gitignore .dockerignore Dockerfile
In the .gitignore
and .dockerignore
files, list .env
as a file to ignore in your project folder.
Github
Create a bare Github repo without any files and copy the repo URL.
Start tracking the changes from your local repo. In the VS Code terminal, run:
git init
git add .
git commit -m "first commit"
git remote add origin <github repo url>
git push -u origin main
Docker
Head over to Docker Desktop and install the program.
This allows you to run Docker commands on your local machine.
Code
We’re finally done with the setup. Time to code. Head back to VS Code. In .env
, set PORT=8000
.
In main.py
, enter the following code:
The default address route will now return you a success message.
Run pipenv run python main.py
in the VS code terminal to start the uvicorn server:
Then go to [http://localhost:8000/docs](http://localhost:8000/docs)
to see if FastAPI is running.

Note that pipenv loads your environment variables, PORT=8000
, for you, before running main.py
.
Build and Run Container Image
Instead of deploying our app as a package, let’s build our app as a Docker image, so that the build process is outlined in a Dockerfile
.
Dockerfile
makes the build process seamless and reproducible.
In the Dockerfile
, write the following:
Build your container and check that the image has been created with the docker images
command.
docker build . -t fastapi-cd:1.0
docker images
Run your container with the image tag you specified earlier, on port 8000.
docker run -p 8000:8000 -t fastapi-cd:1.0
Check your containerized application by going to [http://localhost:8000/docs](http://localhost:8000/docs)
.
You should see the same FastAPI application running.
Congratulations! You just built a simple FastAPI application, and containerized it using a Docker container on your local machine.
Infrastructure setup
Azure Container Registry
Time to deploy our application in Azure. First off, create an Azure Container Registry.

Enable the admin user option in the Access keys pane.

Going back to your VS Code terminal, log in to Azure with your account.
az login
az acr login --name fastapicd
Build and push your Docker image to the registry server.
docker build . -t fastapicd.azurecr.io/fastapi-cd:1.0
docker images
docker push fastapicd.azurecr.io/fastapi-cd:1.0

Check that your Docker image has been successfully pushed to a repo in the registry.

Azure App Service
Next, create an App Service resource.

For the App Service Plan, change the size to Standard S1 SKU under the Production Workloads tab.

On the Docker configuration tab, make sure the container image you pushed earlier is selected.

Once resource deployment is complete, check that your web app is running by going to [https://<your-webapp-name>.azurewebsites.net/docs](https://<your-webapp-name>.azurewebsites.net/docs)
.
You should see the FastAPI swagger docs page render.
Github Actions
Now we’re ready to create our Github Actions .yml file that will automate deployment of our FastAPI application.
In VS Code, create a .github/workflows
directory and a prod.workflow.yml
file within.
mkdir .github
cd .github
mkdir workflows
cd ..
touch .github/workflows/prod.workflow.yml
Build your prod.workflow.yml
file like below:
Now, whenever we do a git push to the main branch, Github Actions will run a deployment job that consists of the 5 steps above, each with its own name.
Push this new file to Github using the git commands:
git add .
git commit -m "github actions deployment workflow"
git push
Service Principal
To automate our deployment workflow with Github Actions, we need to give the actions runner a service principal to authenticate to Azure, and perform the app deployment.
In VS Code terminal, run:
az ad sp create-for-rbac --name "github-actions" --role contributor --scopes /subscriptions/<GUID>/resourceGroups/fastapi-cd --sdk-auth
You can get the subscription GUID from the subscription you used to create the resource group.
You will get a response like below:
Copy the response and save it. You won’t be able to see it again.
Head over to Github settings, and create 3 Github Secrets:
- AZURE CREDENTIALS: Entire JSON response from above.
- REGISTRY_USERNAME: clientId value from JSON response.
- REGISTRY_PASSWORD: clientSecret value from JSON response.

Configure App Service to use Github Actions for continuous deployment
In the Deployment Center pane of the App Service, link your Github repo main branch, and configure deployment to use Github Actions.

Create staging deployment slot
The current app is running in a production slot. To guarantee higher-quality deployments, we will create a staging slot.

Slots are useful for multiple reasons:
- Changes can be released to a smaller % of users in a staging slot, to validate new releases before swapping the staging slot with production.
- Staging slot app instances are warmed up before being swapped into production, guaranteeing that the app is responsive to requests.
- If something goes wrong with the new release, swaps can be performed again to roll back the changes.
Check that the staging app is running like the production app by going to [https://<your-webapp-name>-staging.azurewebsites.net/docs](https://<your-webapp-name>-staging.azurewebsites.net/docs)
.
Configure the same deployment center settings for the staging slot as you did for production.
Push new releases to the staging slot.
In your main.py file, make a change to the return message.
return {"data": "Application ran successfully - FastAPI release v2.0"}
Do a git push to main branch to trigger the Github Actions workflow.
git add .
git commit -m "FastAPI v2.0 release"
git push
Github Actions will build and deploy the new version of your application to the staging slot automatically.

Check the new version of your application by going to [https://<your-webapp-name>-staging.azurewebsites.net/docs](https://<your-webapp-name>-staging.azurewebsites.net/docs)
.
Send a request to the default route to check the response. You should see v2.0 in the return message.

When you’re comfortable swapping the staging app over to production, simply click on Swap in the Deployment Slots pane of the App Service.

Do the same verification of the new app version as above by going to the production URL [https://<your-webapp-name>.azurewebsites.net/docs](https://<your-webapp-name>.azurewebsites.net/docs)
.
Conclusion
Velocity matters when you’re developing software applications.
As developers, there’s a lot of things we have to keep up with – provisioning infrastructure, spinning up new environments, application testing, etc.
If we automate the boring stuff, we can focus our time on the most key tasks – building new features.
In this article, we automated the release and deploy phase of the DevOps lifecycle with Github Actions. This gives us confidence to release often into production without deployment failures.

Resources
All code written in this article can be found at the Github repo:
I would like to give a big shoutout to all the extensive documentation and YouTube videos that helped to complete this article:
- Deploy a custom container to App Service using Github Actions
- Github Action repo – deploy to App Service
- Set up staging slots in App Service
- Learn Docker in 7 Easy Steps – Full Beginner’s Tutorial
- Containerize FastAPI app with Docker
- Push Docker image to Azure Container Registry
- pipenv tutorial video
- How to run pipenv in Docker container