Python Runtime
Host Python applications on Witchly.host. Python 3.13, pip installs, requirements.txt support, Git integration, and dedicated resources.
languages (12 articles)
On This Page
Python Runtime
The Python egg runs Python 3.13 via the ghcr.io/ptero-eggs/yolks:python_3.13 image. Deploy a Git repo or upload your code manually; the egg handles pip install -r requirements.txt and runs your entrypoint.
Quick deploy
- dash.witchly.host → Deploy → Languages → Python.
- On the Startup tab, set:
Git Repo Address— e.g.https://github.com/you/your-bot.gitApp py file— entrypoint (defaultapp.py)Requirements file— defaults torequirements.txt
- Start.
Startup flow
# Pull latest if AUTO_UPDATE=1
if [[ -d .git ]] && [[ "{{AUTO_UPDATE}}" == "1" ]]; then git pull; fi
# Install extra packages (ad-hoc)
if [[ ! -z "{{PY_PACKAGES}}" ]]; then pip install -U --prefix .local {{PY_PACKAGES}}; fi
# Install from requirements.txt if present
if [[ -f /home/container/{{REQUIREMENTS_FILE}} ]]; then
pip install -U --prefix .local -r {{REQUIREMENTS_FILE}}
fi
# Run the app
python3.13 /home/container/{{PY_FILE}}
Packages install to .local/ (user site-packages) so your repo stays clean.
Variables reference
| Variable | Purpose |
|---|---|
GIT_ADDRESS | Git repo URL |
BRANCH | Git branch |
USER_UPLOAD | 1 to skip clone, upload via SFTP |
AUTO_UPDATE | 1 to git pull on each start |
PY_FILE | Entrypoint (default app.py) |
PY_PACKAGES | Extra pip packages (space-separated) |
REQUIREMENTS_FILE | Path to requirements file (default requirements.txt) |
USERNAME, ACCESS_TOKEN | Private repo credentials |
Dependencies
Ship a requirements.txt in your repo. The egg installs it automatically on each boot (after git pull):
discord.py==2.4.0
aiohttp>=3.9
python-dotenv
redis
Or for faster cold starts, commit a poetry.lock / uv.lock and run your own install command.
Environment variables (secrets)
Use python-dotenv with a .env file (don’t commit it — add to .gitignore):
from dotenv import load_dotenv
import os
load_dotenv()
token = os.environ["DISCORD_TOKEN"]
Upload .env via SFTP to /home/container/.
discord.py example
# app.py
import os
import discord
from dotenv import load_dotenv
load_dotenv()
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
@client.event
async def on_ready():
print(f"Logged in as {client.user}")
@client.event
async def on_message(msg):
if msg.content == "!ping":
await msg.channel.send("pong")
client.run(os.environ["DISCORD_TOKEN"])
Web server (FastAPI / Flask)
Your server has a dedicated port allocated. Access it via $SERVER_PORT:
# main.py — FastAPI
from fastapi import FastAPI
import os
app = FastAPI()
@app.get("/")
def read_root():
return {"hello": "world"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=int(os.environ["SERVER_PORT"]))
Set PY_FILE = main.py and your API is live at your server’s IP:PORT.
Troubleshooting
- “ModuleNotFoundError” — add the module to
requirements.txtorPY_PACKAGESand restart. - “Permission denied” on pip install —
--prefix .localis used; if you see this, ensure the egg isn’t overridden by a custom install script. - Async code hangs on start — make sure your entrypoint
asyncio.run()s or callsbot.run(...).
Next steps
- Use Redis for caching or job queues
- Add a scheduled restart to clear memory leaks