Using GitHub Codespaces to Lock Down a Python Development Environment
Learn how to define a reproducible Python environment in GitHub Codespaces with a devcontainer.json, install packages automatically, and verify forwarded ports—without manual setup.
28 Nov 2025, 02:54 UTC

The problem: inconsistent local setups waste time
When a team starts a new feature, each developer often spends minutes—or even hours—installing the correct Python version, dependencies, and debugging version mismatches. This manual drift leads to "it works on my machine" issues and slows down onboarding.
Thesis: a version‑controlled devcontainer.json gives you a reproducible environment instantly
By committing a .devcontainer/devcontainer.json file to your repository, GitHub Codespaces can build the exact container image, install extensions, and run post‑create commands every time a codespace is started. The result is a disposable, identical workspace for every contributor.
Understanding the devcontainer.json file
The file lives in the repository’s .devcontainer folder and tells Codespaces which base image to use, which VS Code extensions to install, and what commands to run after the container is created. It is written in JSON with comments (via JSONC) for clarity.
Worked example: Python 3.11 with a custom package and Flask
Suppose you want every codespace to have Python 3.11, the requests library, and a simple Flask app ready to test.
- Create the folder and file:
mkdir -p .devcontainer
code .devcontainer/devcontainer.json
- Add the following content (replace
<your‑package>with any extra pip package you need):
{
"name": "Python 3.11 with Requests",
"image": "mcr.microsoft.com/vscode/devcontainers/python:3.11",
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance"
],
"postCreateCommand": "pip install requests flask",
"forwardPorts": [5000],
"remoteEnv": {
"FLASK_APP": "app.py"
}
}
- Commit the file and push to GitHub.
Now, from the repository page:
- Click the
Codebutton, chooseOpen with Codespaces, and clickNew codespace. - GitHub will pull the
mcr.microsoft.com/vscode/devcontainers/python:3.11image, start the container, and run thepostCreateCommand.
Inside the running codespace, open a terminal and verify the setup:
# Check Python version
python --version
# Expected output: Python 3.11.x
# Confirm the package is installed
pip list | grep -E "requests|flask"
# Test import
python -c "import requests, flask; print('OK')"
To see the Flask app in action, create a minimal app.py in the repository root:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello from Codespaces!'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Start the app:
flask run
In the Codespaces UI, open the Ports tab. You should see port 5000 forwarded with a URL like https://<random>-5000.github.dev. Opening that URL in a browser displays the greeting, confirming that port forwarding and the environment are working.
Trade‑offs and limitations
- First‑launch latency: The initial codespace start can take several minutes as the image is pulled and the
postCreateCommandruns. Subsequent starts are faster if the image remains cached. - Storage limits: The default codespace storage is modest; large dependency caches or build artifacts may exceed it. You can clean up with
docker system pruneinside the codespace or request an increase via GitHub Support. - Image selection: Using a Microsoft‑maintained image gives quick starts but less control. For full control, point
imageto a custom Dockerfile, which increases build time.
Actionable closing: adopt the pattern today
Add a .devcontainer/devcontainer.json to any repo that needs a stable dev environment. Start with a base image that matches your language, list essential extensions, and put dependency installation in postCreateCommand. Verify the setup with a quick version check and a port‑forwarded test service. Over time, the team will spend less time on environment wrangling and more time on writing code.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.