Deployment
Deployment
How to run Convexfolio on a server so it works automatically — once a day, on a schedule, or behind a Docker container.
📖 New to servers? See the Glossary for terms like
systemd,cron,Docker.
What is deployment?
“Deployment” means running Convexfolio on a machine that isn’t your laptop — a server that runs 24/7, runs the package on a schedule, and saves the reports somewhere you can fetch them.
There are three common ways:
| Style | Best for |
|---|---|
| systemd service | Linux servers. Runs once at boot or on demand. |
| cron job | Linux servers. Runs on a recurring schedule (e.g., daily). |
| Docker container | Any OS. Bundles the package + dependencies into one image. |
If you don’t know which to pick, start with cron — it’s the simplest.
Before you start
You’ll need:
- A Linux server (a VM, a Raspberry Pi, an AWS EC2 instance, etc.).
- Python 3.12 or newer installed.
- Root or sudo access (so you can install packages and write to system folders).
A non-root user to run the package as — don’t run things as root unless you have a specific reason. Create a dedicated user:
sudo useradd --system --shell /bin/bash --home /opt/convexfolio convexfolio
Option 1 — cron (simplest)
cron is Linux’s built-in scheduler. You give it a line that says
“run this command at this time, every day/week/etc.”
Step 1 — install the package
sudo -u convexfolio -H bash -c '
cd /opt/convexfolio
git clone https://github.com/sachncs/convexfolio.git .
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
'
(-H makes sure the user’s home directory is used as ~; -u
switches to the convexfolio user.)
Step 2 — write the config
sudo -u convexfolio mkdir -p /opt/convexfolio/config
sudo tee /opt/convexfolio/config/config.json > /dev/null <<'EOF'
{
"runtime": {
"seed": 7,
"log_level": "INFO",
"output_directory": "/var/lib/convexfolio/site/artifacts"
},
"optimization": {
"alpha": 0.05,
"method": "all",
"enforce_nu_greater_than_six": true
}
}
EOF
sudo chown convexfolio:convexfolio /opt/convexfolio/config/config.json
Step 3 — schedule it
Open the crontab for the convexfolio user:
sudo -u convexfolio crontab -e
Add a line:
# Run every day at 2:00 AM
0 2 * * * cd /opt/convexfolio && .venv/bin/convexfolio --config config/config.json --command reproduce-report >> /var/log/convexfolio.log 2>&1
Format: minute hour day-of-month month day-of-week command. The
above runs daily at 2 AM. Save and exit.
Step 4 — verify
Check the log:
tail -n 50 /var/log/convexfolio.log
ls /var/lib/convexfolio/site/artifacts/
You should see report.json written after the first run.
Option 2 — systemd (for one-off or boot-time runs)
systemd is Linux’s built-in service manager. It’s how every Linux
daemon runs. (Glossary: systemd)
Step 1 — install the package
Same as cron, above.
Step 2 — write the service file
Create /etc/systemd/system/convexfolio.service:
[Unit]
Description=Convexfolio service
After=network.target
[Service]
Type=oneshot
User=convexfolio
Group=convexfolio
WorkingDirectory=/opt/convexfolio
ExecStart=/opt/convexfolio/.venv/bin/convexfolio \
--config /opt/convexfolio/config/config.json \
--command reproduce-report
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Format: standard systemd unit file. (Glossary: systemd)
Step 3 — enable and run
sudo systemctl daemon-reload # pick up the new file
sudo systemctl start convexfolio # run it now
sudo systemctl status convexfolio # check it worked
sudo systemctl enable convexfolio # (optional) run at boot
Logs go to the journal:
sudo journalctl -u convexfolio -n 50
Option 3 — Docker (for portability)
Docker packages Convexfolio + its dependencies into one image that runs the same on every machine. (Glossary: Docker)
Step 1 — write the Dockerfile
FROM python:3.12-slim
WORKDIR /app
# Install the package.
COPY pyproject.toml ./
COPY convexfolio ./convexfolio
RUN pip install --no-cache-dir .
# Copy config.
COPY config.json /app/config.json
# Default command.
CMD ["convexfolio", "--config", "/app/config.json", "--command", "reproduce-report"]
Step 2 — build the image
docker build -t convexfolio:latest .
Step 3 — run the container
# One-off run, with reports saved to ./site/artifacts on the host
docker run --rm \
-v "$(pwd)/site/artifacts:/app/site/artifacts" \
convexfolio:latest
For a scheduled Docker run, you’d typically combine this with cron on
the host (yes, you can have cron run docker run ...) or use a
container orchestrator like Kubernetes. (Glossary: Docker)
Output management
Whichever option you pick, the package writes to
runtime.output_directory. Default is site/artifacts/. Pick a path
that’s:
- On a persistent volume (not in
/tmpor container scratch space). - Backed up, if you care about historical reports.
- Writable by the user the package runs as.
A common pattern: timestamp the output directory:
0 2 * * * cd /opt/convexfolio && \
mkdir -p "site/artifacts/$(date +\%Y-\%m-\%d)" && \
sed -i "s|output_directory.*|output_directory: \"site/artifacts/$(date +\%Y-\%m-\%d)\"|" config/config.json && \
.venv/bin/convexfolio --config config/config.json --command reproduce-report
(That snippet overwrites the config each day so reports are split into daily folders. Adapt to your needs.)
Environment variables
The package doesn’t read many environment variables directly, but a few are useful:
| Variable | Effect |
|---|---|
OPTIONS_PARALLEL_THRESHOLD |
In Report.from_reproduce, switch to a process pool when repetitions exceed this. Default 4. |
PYTHONPATH |
Where Python looks for modules. The systemd / cron examples already set this via the venv. |
To set them in systemd, add Environment=KEY=value lines under
[Service]. To set them in cron, prefix the command:
0 2 * * * OPTIONS_PARALLEL_THRESHOLD=8 /opt/convexfolio/.venv/bin/convexfolio ...
Monitoring
A few questions to ask yourself once it’s running:
- Did the run succeed? Check the exit code (
0= success, non-zero = failure). Cron logs the exit code in some setups; systemd makes it available viasystemctl status. - Where are the reports?
ls $runtime.output_directory. They should appear after each scheduled run. - Is the log file growing?
ls -la /var/log/convexfolio.log. If it stops growing, the job isn’t running.
For serious monitoring, pipe the output to a log-collector (Loki, Datadog, etc.) — but that’s out of scope for this guide.
Troubleshooting
The cron job runs but produces no output
Check that:
- The cron job’s
cdmatches where you actually installed the package. - The user
cronruns as can read the config and write to the output directory. - The path to the
convexfoliobinary is absolute (cron has a tinyPATH; don’t rely on relative paths).
The systemd service fails immediately
Check journalctl -u convexfolio -xe for the error. Common causes:
- Missing Python module — re-run
pip install -e .inside the venv. - Wrong working directory in the unit file.
- Permissions on the config or output directory.
Docker container can’t write site/artifacts
The -v "$(pwd)/site/artifacts:/app/site/artifacts" mount makes the host
folder available inside the container. If the host folder doesn’t
exist, Docker creates it as root, and the container (running as a
non-root user) can’t write to it. Fix:
mkdir -p ./site/artifacts
chmod 777 ./site/artifacts # or chown to match the container's user
Where to look next
- Glossary — Every term used here.
- Release process — How to publish a new version after fixing things in production.
- Architecture — How the package is put together.