Python Runtime

Host Python applications on Witchly.host. Python 3.13, pip installs, requirements.txt support, Git integration, and dedicated resources.

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

  1. dash.witchly.hostDeployLanguagesPython.
  2. On the Startup tab, set:
    • Git Repo Address — e.g. https://github.com/you/your-bot.git
    • App py file — entrypoint (default app.py)
    • Requirements file — defaults to requirements.txt
  3. 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

VariablePurpose
GIT_ADDRESSGit repo URL
BRANCHGit branch
USER_UPLOAD1 to skip clone, upload via SFTP
AUTO_UPDATE1 to git pull on each start
PY_FILEEntrypoint (default app.py)
PY_PACKAGESExtra pip packages (space-separated)
REQUIREMENTS_FILEPath to requirements file (default requirements.txt)
USERNAME, ACCESS_TOKENPrivate 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.txt or PY_PACKAGES and restart.
  • “Permission denied” on pip install--prefix .local is 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 calls bot.run(...).

Next steps