Implementing Supercronic in a Django app

Hey folks, thanks for all the comments I got on my previous question about using Supercronic. I’m walking through the tutorial and I’ve got three questions about the actual implementation, after reading the Crontab with Supercronic help article.

Calling the right command from the crontab

I’ve added the crontab file to my project, and since I want to run it every 5 minutes this is what it looks like.

*/5 * * * * python mysite/manage.py overdue_checkouts

Is that the correct command syntax, or do I need to use the full path to python? If the latter, how do I find the python path for the machine? Google says that for the docker image I’m using, 3.12-slim-bookworm, that path would be /usr/local/bin/python. So would the crontab text be?

*/5 * * * * /usr/local/bin/python mysite/manage.py overdue_checkouts

Adding the supercronic bits to my Dockerfile

I’m not terribly familiar with Docker. I’ve added the bits for supercronic, but are they in the right order?

ARG PYTHON_VERSION=3.12-slim-bookworm

FROM python:${PYTHON_VERSION}

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.2.29/supercronic-linux-amd64 \
    SUPERCRONIC=supercronic-linux-amd64 \
    SUPERCRONIC_SHA1SUM=cd48d45c4b10f3f0bfdd3a57d054cd05ac96812b


RUN mkdir -p /code

WORKDIR /code

COPY requirements.txt /tmp/requirements.txt

RUN set -ex && \
    pip install --upgrade pip && \
    pip install -r /tmp/requirements.txt && \
    rm -rf /root/.cache/

# install psycopg2 dependencies
RUN apt-get update && apt-get install -y \
    libpq-dev \
    gcc \
    && rm -rf /var/lib/apt/lists/*  # <-- Updated!

RUN curl -fsSLO "$SUPERCRONIC_URL" \
 && echo "${SUPERCRONIC_SHA1SUM}  ${SUPERCRONIC}" | sha1sum -c - \
 && chmod +x "$SUPERCRONIC" \
 && mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" \
 && ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic

COPY . /code/
COPY crontab crontab

ENV PYTHONPATH="/code/mysite:$PYTHONPATH"

RUN python mysite/manage.py collectstatic --noinput

EXPOSE 8000

# replace demo.wsgi with <project_name>.wsgi
CMD ["gunicorn", "--bind", ":8000", "--workers", "2", "mysite.wsgi"]

Updating my fly.toml file

In the Setup a web & cron process section of the help doc, after adding the processes block to my fly.toml file, it says to “replace with the command you’re using to launch your server”.

[processes]
  # The command below is used to launch a Rails server; be sure to
  # replace with the command you're using to launch your server.
  web = "bin/rails fly:server"
  cron = "supercronic /app/crontab"

I’m not sure what command that would be. Is that the gunicorn call in my Dockerfile (snipped out here)?

CMD ["gunicorn", "--bind", ":8000", "--workers", "2", "mysite.wsgi"]

Then it says that I have to “tell Fly that your web process matches up with a service by having this under the [[services]].”

[[services]]
  processes = ["web"]

Here’s my entire fly.toml file. My services.processes property is currently set to [“app”]. Does the above statement mean that I should change “app” to “web”?

# fly.toml file generated for nashville-tabletop-day on 2023-04-10T13:44:04-05:00

app = "nashville-tabletop-day"
kill_signal = "SIGINT"
kill_timeout = 5
primary_region = "dfw"
processes = []

[env]
  PORT = "8000"

[experimental]
  auto_rollback = true

[deploy]
  release_command = "python mysite/manage.py migrate --noinput"

[[services]]
  http_checks = []
  internal_port = 8000
  processes = ["app"]
  protocol = "tcp"
  script_checks = []
  [services.concurrency]
    hard_limit = 25
    soft_limit = 20
    type = "connections"

  [[services.ports]]
    force_https = true
    handlers = ["http"]
    port = 80

  [[services.ports]]
    handlers = ["tls", "http"]
    port = 443

  [[services.tcp_checks]]
    grace_period = "1s"
    interval = "15s"
    restart_limit = 0
    timeout = "2s"

[[statics]]
  guest_path = "/code/static"
  url_prefix = "/static/"

[processes]
  # The command below is used to launch a Rails server; be sure to
  # replace with the command you're using to launch your server.
  web = "bin/rails fly:server"
  cron = "supercronic /app/crontab"

Assuming Docker is installed on your dev machine, do docker pull python:3.12-slim-bookworm, then docker run -it python:3.12-slim-bookworm sh, then in the container shell, which python. That will give you a path for your binary.

(Some distros prefer python3 as a command, feel free to try that too).

Right… It needs to match one of the keys in the [processes] stanza. This is connecting your Django server to the Fly Proxy, and thence to the public Internet.

Yes, the values in quotes under [processes] override the CMD from the Dockerfile, basically.

https://fly.io/docs/reference/configuration/#the-processes-section

Probably you also want "supercronic /code/crontab" for the cron line, since your Dockerfile copied crontab into /code/ instead of /app/ (due to WORKDIR).