TwitterAutoPost
A small containerized app that posts one random photo a day, taken from a folder on my NAS, to a Twitter/X account — all driven by a single YAML config file.
The idea
I had a folder of photos on my NAS and wanted one random picture posted per day to a Twitter/X account, without ever thinking about it. The result is a small app in a Docker container: turn it on once and it runs itself.
What it does
- Picks a random photo on every run (repeats allowed — no state kept).
- Recurses into subfolders, with an exclude list, and filters by file extension.
- Configurable time and timezone for the daily post.
- Optional caption (fixed text and/or the file name).
- Command-line conveniences:
--dry-run(pick and print without posting) and--once(post immediately once, then exit).
How it works
The container runs an in-process scheduler: it computes the next occurrence of the
configured time in the chosen timezone, sleeps until then, picks an eligible image and
posts it. The upload uses Twitter’s v1.1 media endpoint, while the tweet itself goes
through the v2 endpoint (via tweepy). On any failure it logs and waits for the next
day, so the daemon stays up; restart: unless-stopped brings it back after a reboot.
Design choices
- Everything in YAML: extensions, exclusions, time, timezone, caption. Secrets (the
API keys) live in the
.envfile instead, which overrides the config — so they never end up in the YAML. - No state: a purely random pick each run, repeats accepted.
- NAS access: the SMB share is mounted on the host and then bind-mounted into the container, rather than mounted inside the container.
- Log rotation delegated to Docker’s
json-filedriver (no systemd or logrotate).
Notes & gotchas
- The X API is paid: posting consumes purchased credits (a plain post costs very little, a post with a link a fair bit more) — worth knowing before automating.
- Uploading an image needs OAuth 1.0a user tokens (not OAuth 2.0), and all four keys must come from the same app, otherwise you get a 401.
- On Docker Desktop with WSL2 it’s best to mount the share as a CIFS volume: a plain bind mount of a UNC path can come up silently empty.
Stack
Python · tweepy · Docker · Twitter/X API