1. What a One-Off Command Is

A web app does most of its work by answering requests. Some jobs do not fit that pattern. You run them once, by hand, when something changes. Creating the first admin user is one. Updating the database after you add a column is another. Fixing a batch of records with a short script is a third.

Developers call these jobs one-off commands. On your laptop, you type them into a terminal next to your code. On a server, you need a way to run the same command next to the deployed code, with the live database and the live secrets.

This guide lists the commands Python developers run most, compares how hosts handle them and shows how to run them safely against live data.

2. Commands You Will Need Most

Django

CommandWhat it does
python manage.py migrateApplies database changes from your migration files
python manage.py createsuperuser --noinputCreates an admin user from environment variables
python manage.py check --deployLists settings that are unsafe for production
python manage.py showmigrationsShows which migrations have run
python manage.py clearsessionsDeletes expired login sessions
python manage.py dumpdata app.Model --output /app/data/export.jsonExports records to a file
python manage.py loaddata /app/data/export.jsonImports records from a file

Flask

CommandWhat it does
flask db upgradeApplies database changes with Flask-Migrate
flask routesLists every URL your app answers
flask seed-productsRuns a command you wrote yourself with @app.cli.command()

Any Python app

CommandWhat it does
python scripts/fix_prices.pyRuns a script from your project
alembic upgrade headApplies database changes with Alembic
pip listShows the packages and versions that are installed
ls -la /app/dataLists the files your app has saved
python -c "import sqlite3; print(sqlite3.sqlite_version)"Runs one line of Python

3. How Different Hosts Run Them

Every host has an answer to this problem. The answers differ in how much setup they need and how close the command runs to your live app.

SSH into a VPS

On your own server, you log in with SSH, move to the project folder, activate the virtual environment and type the command. You have full control. You also have full responsibility. Nothing stops a typing mistake, and nothing records what you ran unless you set that up.

ssh me@203.0.113.10
cd /srv/shop && source .venv/bin/activate
python manage.py migrate

A command-line tool from the host

Heroku made this pattern popular with heroku run. The tool starts a temporary copy of your app, runs the command there and removes the copy. Other hosts offer similar tools. You install the tool, log in from your terminal and learn its options.

A console in the browser

Some hosts, such as PythonAnywhere, give you a Bash console in the browser. It behaves like SSH without the setup. It suits exploring, but long sessions can time out, and commands you typed last month are hard to find again.

A Run a command box

Cloudilio takes the simplest route. Each app has a box where you type one command and press Run. The command runs in a fresh, temporary copy of your app, and the page shows what it printed. There is no tool to install and no key to manage. Every command is saved with its output, the time and the person who ran it.

4. Why Commands Cannot Ask Questions

On your laptop, python manage.py createsuperuser asks for a username, an email and a password. On most hosts, a one-off command has no keyboard attached. A command that waits for an answer fails or waits until its time runs out.

The fix is to give every answer up front. Most tools have an option for this.

ToolOption that skips the questions
Django commands--noinput
pip uninstall--yes
Your own scriptsRead values from options or environment variables, never from input()

Django's createsuperuser is the most common example. With --noinput, it reads the password from an environment variable named DJANGO_SUPERUSER_PASSWORD. Save that value as a secret first, then run the command.

python manage.py createsuperuser --noinput --username admin --email you@example.com

Delete the secret after the user exists. Then sign in and change the password from the admin site.

5. Run a Command on Cloudilio

  1. Open your app's Tasks page. The Run a command box is at the top. Your app must be published first, because the command runs in the published version.
  2. Type the command. Write it the way you would in a terminal, such as python manage.py migrate. The buttons under the box fill in common commands for your framework.
  3. Pick a time limit. The choices are 1 minute, 5 minutes, 15 minutes and 1 hour. Cloudilio stops the command when the time is up.
  4. Press Run. The page shows a spinner while the command runs and updates when it finishes.
  5. Read the result. You see everything the command printed, how long it took and whether it finished or failed. A failed command also shows its exit code.

A few facts explain how the command behaves.

  • It runs beside your app, not inside it. Cloudilio starts a temporary copy of your app for the command and removes the copy afterwards. Your website keeps answering visitors the whole time.
  • It sees the same things your app sees. The copy has your code, your packages, your secrets and your data folder at /app/data. A migration changes the same database your site reads.
  • Only the data folder keeps changes. A file the command writes to /app/data stays. A file it writes anywhere else disappears with the temporary copy.
  • One command runs at a time for each app. This stops two migrations from running at once.
  • Each app keeps its last 20 commands. Open Earlier commands to read the output of any of them again.

Chain two commands

The box runs one program. It does not understand shell symbols such as &&, | or >. To use them, hand the whole line to a shell with sh -c.

sh -c "python manage.py migrate && python manage.py clearsessions"

6. Command, Publish Step, Schedule or Worker

Cloudilio has four ways to run code outside a web request. Each one answers a different question about timing.

ToolWhen it runsGood for
Run a commandOnce, when you press RunCreating an admin user, a one-time data fix, checking something
Run before each publishEvery time you publish, before the new version goes liveDatabase migrations that must match the new code
Scheduled taskOn a timetable, such as every nightReports, cleanups, backups to another service
Background workerAll the time, beside your websiteSlow jobs your pages hand off, such as sending email

A good rule for migrations is to put python manage.py migrate --noinput in Run before each publish. Then the database always matches the code. Keep Run a command for jobs that happen once or rarely.

7. Six Habits for Safe Commands

A one-off command works on live data. These habits prevent most accidents.

  1. Make a backup first. Before a command that changes or deletes data, create a backup on the Data & backups page. A restore takes less time than rebuilding lost records.
  2. Print before you change. Give data-fix scripts a mode that only prints what they would do. Run that mode first and read the output.
  3. Write scripts you can run twice. A script that stops halfway should be safe to start again. Check whether each record still needs the change before you change it.
  4. Keep secrets out of the command. The command is saved in the history. Put passwords and API keys on the Secrets page and read them from environment variables.
  5. Choose a time limit that fits. A check should finish in a minute. If it runs longer, something is wrong, and a short limit stops it early.
  6. Print progress. A script that prints a line for every thousand records shows how far it got if it fails.

This small script follows all six habits. It lowers prices by ten percent, once, and reports what it did.

import os
import sqlite3
import sys

dry_run = "--apply" not in sys.argv
db = sqlite3.connect(os.path.join(os.environ["APP_DATA_DIR"], "shop.db"))

rows = db.execute("SELECT id, price FROM products WHERE discounted = 0").fetchall()
print(f"{len(rows)} products need the discount")

for number, (product_id, price) in enumerate(rows, start=1):
    if not dry_run:
        db.execute(
            "UPDATE products SET price = ?, discounted = 1 WHERE id = ?",
            (round(price * 0.9, 2), product_id),
        )
    if number % 1000 == 0:
        print(f"{number} done")

db.commit()
print("Dry run, nothing changed" if dry_run else "Finished")

Run python fix_prices.py to see the count. Run python fix_prices.py --apply to make the change. A second run finds nothing left to do.

8. Questions People Ask

How do I run Django migrations without SSH?

Use your host's one-off command feature. On Cloudilio, open the Tasks page, type python manage.py migrate under Run a command and press Run. To run migrations with every publish, put the same command in Run before each publish.

How do I create a Django superuser on a host with no terminal?

Save DJANGO_SUPERUSER_PASSWORD as a secret. Then run python manage.py createsuperuser --noinput --username admin --email you@example.com as a one-off command. Delete the secret afterwards.

Does a one-off command slow down my site?

It runs in its own temporary copy with its own memory limit, so it does not take memory from your website. A command that writes to the database for a long time can make pages wait, so keep heavy writes in small batches.

Can I open a Python shell?

Not an interactive one, because the command has no keyboard attached. Run a single line with python -c "...", or put the code in a script and run the script.

What happens if a command runs out of time?

Cloudilio stops it and marks it as stopped. You still see everything it printed up to that point. Choose a longer limit, up to one hour, or split the work into smaller steps.

Cloudilio Engineering Team
Application Delivery Group • Cloudilio

Cloudilio hosts Python apps. You publish Flask, Django, FastAPI, Streamlit and plain Python scripts without managing a server, and your app's data stays safe between updates.