Feb 19, 2025
Supercronic Cron jobs for SvelteKit Apps on Fly.io
Fly.io’s documentation includes a Crontab with Supercronic page that details how to set up container-friendly cron jobs for your Fly.io app using Supercronic. I found it to be very generic though, and I had to modify lots of things to make it work for my SvelteKit app. Here’s what I ended up with:
1. Run touch ./crontab
to create a crontab file in the root of your project and define your cron jobs.
The example below shows how to include environment variables in your cron jobs and log errors to a .log
file.
# Environment variables
AUTH_TOKEN="Bearer ABC123"
BASE_URL="https://influencermarketingjobs.net"
# Cron jobs
*/15 * * * * curl -s -H "Authorization: $AUTH_TOKEN" "$BASE_URL/api/cron-test" >> /data/cron-test.log 2>&1
2. Update Dockerfile to add Supercronic
Here’s what I ended up with:
# syntax = docker/dockerfile:1
# Adjust NODE_VERSION as desired
ARG NODE_VERSION=22.13.0
FROM node:${NODE_VERSION}-slim AS base
LABEL fly_launch_runtime="SvelteKit"
# SvelteKit app lives here
WORKDIR /app
# Set production environment
ENV NODE_ENV="production"
# Throw away build stage to reduce size of final image
FROM base AS build
# Install packages needed to build node modules and use curl
RUN apt-get update -qq &&
apt-get install --no-install-recommends -y
build-essential
node-gyp
pkg-config
python-is-python3
curl
ca-certificates &&
rm -rf /var/lib/apt/lists/*
# Create .env file for build-time environment variables
# Environment variables must be passed in when `fly deploy` is run.
RUN
echo "SCRAPE_JOBS_API_SECRET=$(cat /run/secrets/SCRAPE_JOBS_API_SECRET)" > .env &&
echo "CRON_SECRET=$(cat /run/secrets/CRON_SECRET)" >> .env &&
echo "SENDGRID_API_KEY=$(cat /run/secrets/SENDGRID_API_KEY)" >> .env &&
echo "ADMIN_EMAIL_ADDRESS=$(cat /run/secrets/ADMIN_EMAIL_ADDRESS)" >> .env &&
export SCRAPE_JOBS_API_SECRET="$(cat /run/secrets/SCRAPE_JOBS_API_SECRET)" &&
export CRON_SECRET="$(cat /run/secrets/CRON_SECRET)" &&
export SENDGRID_API_KEY="$(cat /run/secrets/SENDGRID_API_KEY)" &&
export ADMIN_EMAIL_ADDRESS="$(cat /run/secrets/ADMIN_EMAIL_ADDRESS)"
# Set Supercronic environment variables
# Latest releases available at https://github.com/aptible/supercronic/releases
ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.2.33/supercronic-linux-amd64
SUPERCRONIC_SHA1SUM=71b0d58cc53f6bd72cf2f293e09e294b79c666d8
SUPERCRONIC=supercronic-linux-amd64
# Install Supercronic
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
# Install node modules
COPY .npmrc package-lock.json package.json ./
RUN npm ci --include=dev
# Copy application code
COPY . .
# Build application
RUN npm run build
# Remove development dependencies
RUN npm prune --omit=dev
# Final stage for app image
FROM base
# Install curl for cron jobs
RUN apt-get update -qq &&
apt-get install -y curl &&
rm -rf /var/lib/apt/lists/*
# Copy built application
COPY /app/build /app/build
COPY /app/node_modules /app/node_modules
COPY /app/package.json /app
COPY /app/crontab /app
COPY /usr/local/bin/supercronic /usr/local/bin/supercronic
# Setup sqlite3 on a separate volume
RUN mkdir -p /data
VOLUME /data
# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
ENV DATABASE_URL="/data/production.db"
CMD [ "npm", "run", "start" ]
This file does some of the things that the Fly.io documentation page does, but it also installs curl
and ca-certificates
during the build, which are necessary to download Supercronic from GitHub. It also installs curl
again in the final stage so that it’s available at runtime when cron jobs execute.
Also note that instead of using COPY crontab crontab
as shown in the Fly.io docs, I use COPY --from=build /app/crontab /app
to copy the crontab
file into the correct directory (/app
).
3. Update fly.toml file
Update your Fly.toml file so that you have one app
process for your app, and a separate cron
process for your cron jobs (see the [processes]
section, below).
I ended up with a fly.toml
file like this:
# fly.toml app configuration file generated for influencer-marketing-jobs on 2025-01-29T22:52:59-05:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = 'influencer-marketing-jobs'
primary_region = 'iad'
[build]
[[mounts]]
source = 'data'
destination = '/data'
auto_extend_size_threshold = 80
auto_extend_size_increment = '1GB'
auto_extend_size_limit = '10GB'
[[vm]]
memory = '1gb'
cpu_kind = 'shared'
cpus = 1
# Define both processes
[processes]
app = "node build"
cron = "supercronic /app/crontab"
# Configure the web service
[[services]]
internal_port = 3000
processes = ["app"]
protocol = "tcp"
auto_stop_machines = 'stop'
auto_start_machines = true
min_machines_running = 0
[[services.ports]]
port = 80
handlers = ["http"]
force_https = true
[[services.ports]]
port = 443
handlers = ["tls", "http"]
Some of this code is based on Fly’s Supercronic docs, but I made some adjustments to make it work with my SvelteKit app.
Note that the processes = ["app"]
means that only the app
process will be exposed to the outside world to serve web requests, whereas the cron
process will not.
This code will result in one Fly machine for your app, and a separate one for your cron jobs.
4. Deploy app
Run fly deploy
to deploy your app, including the environment variables as shown below.
fly deploy
--build-secret SCRAPE_JOBS_API_SECRET=ABC123
--build-secret CRON_SECRET=ABC123
--build-secret RESEND_API_KEY=re_ABC123
--build-secret ADMIN_EMAIL_ADDRESS=[email protected]
5. Scale app
Run fly scale count cron=1 app=1
to scale your cron
and app
processes to one instance each. If you have a setup that requires more instances of app
, you can adjust that number as needed.
6. Testing
To test this, create a few SvelteKit API endpoints in your app, then adjust your crontab
file to hit them via curl
.
You can secure your API endpoints by checking for the CRON_SECRET
header value (which we set in the crontab
example file, above), like this:
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { CRON_SECRET } from '$env/static/private';
export const GET: RequestHandler = async ({ request }) => {
const authHeader = request.headers.get('authorization');
if (authHeader !== `Bearer ${CRON_SECRET}`) {
error(401, 'Unauthorized');
}
return json({ message: 'API endpoint was hit successfully!' });
}
If cron jobs don’t seem to be working, check the Live Logs for your machine in the Fly.io dashboard when the cron job runs to see if any errors are being logged.
You can also run fly ssh console
to SSH into your Fly machine, then cat /data/cron-test.log
to see what’s been logged to the cron job log file.
Conclusion
You should now have cron jobs set up for your SvelteKit app on Fly.io. Enjoy!