Part 1 of 7 · Social scheduler series ~5 min read

A social scheduler on AWS for a few dollars a month

A small business has more posts to push out than anyone wants to do by hand at 9am every day. The Monday tip for Facebook. The Tuesday offer for Instagram. The job-opening for LinkedIn that someone keeps meaning to write. The owner drafts them in spare minutes, then forgets to actually post them — or posts a draft that was never finished, or one that’s too long for the channel it landed on. This post walks through the design of a small scheduler that lets you draft every post in one place, posts each one at the right time to the right channels, and tells you afterward what went out and what failed.

Key takeaways

  • Three sources for posts: a Drive sheet, an optional draft-helper, and a recurring-template lane.
  • Every post ends in one of four moves on each tick: resting, queue, send, or retry.
  • Per-channel format rules: character limits, image sizes, and link counts are checked before anything is queued.
  • Nothing posts until a human approves it. A half-finished draft never goes out by accident.
  • Designed on AWS for about $2/month at typical small-business volume.

The whole system on one page

Before any code, here’s the shape of what we’re designing.

System architecture: three sources, three pieces inside AWS At the top, three external boxes in a row. Far left, "Draft posts" — a Google Drive sheet listing each post with its text, the channels to send to, the scheduled time, and a link to an image in the same folder, plus an optional draft-helper lane and a recurring-template lane that add posts to the sheet. Centre, "Rules and channels" — a Drive folder with a rules doc covering per-platform format limits, posting windows, and the channel list, plus a voice doc with the brand tone. Far right, "Channels" — the social accounts the posts go to, such as Facebook, Instagram, and LinkedIn; the owner gets a report of what went out and what failed. Each connects via an arrow to the AWS account container below. Draft posts have an outgoing arrow into AWS. Rules and channels feed in to check every post. The owner receives a report with the post, the channels it reached, any failures, and a link to the live post. Inside the AWS account are three components in a row, mirroring the layout above. On the left, the Post intake — receives posts from each source, runs the optional draft-helper via Bedrock, and writes the cleaned post into the sheet. In the middle, the Scheduler — runs daily; reads the calendar; computes when each approved post is due; picks one of four moves: resting, queue, send, or retry. On the right, the Sender — checks the format per channel, posts to each chosen channel at the scheduled minute, and records the result. Internal arrows flow left to right. A note at the bottom reads: nothing posts until a human approves it — the scheduler never sends an unfinished draft. Draft posts sheet, helper, templates Rules and channels limits, windows, tone Channels where posts land posts in checks post out plus report AWS account Post intake draft, normalize, add to sheet Scheduler picks one of four: resting, queue, send, retry Sender checks format, posts per channel post due Nothing posts until a human approves it — the scheduler never sends an unfinished draft.
Fig 1. Three sources outside, three pieces inside AWS. Posts flow in from a Drive sheet, an optional draft-helper, and a recurring-template lane. The Scheduler runs daily and picks one of four moves. The Sender posts each one to the right channel at the right time.

What you set up once (the outside)

  • Draft posts. A Google Sheet in a Drive folder, one row per post: the text, the channels to send to (Facebook, Instagram, LinkedIn, and so on), the scheduled date and time, a link to the image in the same folder, and an approval flag. You can fill it in whenever you have a spare minute; posts can also enter via two other lanes covered in Part 2 — a draft-helper lane (type a short note and the helper roughs out the post text for one-tap approval) and a recurring-template lane (standing posts like “Friday hours reminder” get dropped onto the calendar on a schedule you set).
  • A rules folder. Two short Google Docs in a Drive folder. The rules doc covers the format limits for each platform — the character count, the image sizes and types the platform accepts, and the link count. It also lists the channels you post to, the posting windows (no posts before 8am or after 8pm by default), and any blackout days. The voice doc holds your brand tone — what the draft-helper should sound like when it roughs out text.
  • Channels. The social accounts you post to. Each channel has a token stored safely in AWS that lets the scheduler post on your behalf. After each post, you get a short report — which channels it reached, a link to the live post, and any channel that failed with the reason why.

What runs on every tick (the inside)

  • The post intake. Three sources feed the sheet. The Drive sheet itself is the canonical store. New posts can also be added via the draft-helper lane (type a short note like “remind people we’re closed Monday” and Bedrock Haiku 4.5 roughs out post text in your brand voice, then drops a one-tap approval card so you confirm before the row is added) and the recurring-template lane (standing posts get dropped onto the calendar by a small Lambda on the schedule you set).
  • The scheduler. Runs once a day at 7am local. Reads the calendar. For each approved post, computes how far away its scheduled time is. Picks one of four moves. Resting: the post is more than a day away or not approved yet — do nothing. Queue: the post is due today — book a one-off send for the exact minute it’s scheduled. Send: the minute has arrived — hand it to the Sender. Retry: a channel failed earlier and is eligible for another attempt — queue it again with a small delay. The scheduler doesn’t call a model on the daily tick — the move logic is plain Python.
  • The Sender. Reads the rules doc, checks the post against the format limits for each chosen channel, and posts it. A post that fails any check is flagged back to the owner with the exact reason instead of being sent. A post that passes is sent to each channel through that channel’s posting interface. Every send writes a row in DynamoDB so the report can tell what went out and what failed. A weekly digest summarizes everything that posted that week, plus what’s coming up. A monthly recap writes a short narrative: count by channel, top posts, anything that failed.

In plain words

You draft a Tuesday-morning offer for Instagram and Facebook on Sunday night. You set the time to 9am Tuesday, drop in an image, and flip the approval flag. The scheduler sees it on Monday’s tick, checks the caption fits Instagram’s limit and the image is the right size for both channels, and books a send for 9am Tuesday. At 9am Tuesday the Sender posts it to both channels and records two links. At 9:05 you get a short report: “Tuesday offer — posted to Facebook and Instagram. [links].” If Instagram had rejected the image for being too small, the report would say exactly that, and nothing would have gone out half-broken.

The cost of running this is about $2 a month at SMB volume. The cost of not running it is the offer that never went out because Tuesday got busy, or the caption that posted truncated mid-sentence, or the image the platform silently dropped.

Design rules that shaped every decision

  • Nothing posts until a human approves it. The approval flag is the one gate every post passes.
  • Four moves, always. Resting, queue, send, retry. There is no fifth.
  • Posting windows and blackout days are respected. A post scheduled for 2am waits for the morning.
  • Every post is format-checked before it’s queued. A post that fails is flagged, not sent.
  • The sheet lives in Drive. Adding a post, changing a channel, or shifting a time doesn’t need a deploy.
  • Every send is logged. Look back next month and you can see every post that went out and every one that failed.

Why this shape

Most small businesses handle social posts in one of three ways: a person who posts by hand and forgets half the time, a third-party scheduling tool that costs a monthly subscription per seat, or a spreadsheet of “things we should post” that nobody opens. The by-hand way fails the first busy week. The paid tool works but charges every month whether you post once or a hundred times. And the spreadsheet, of course, is just a list of good intentions.

The setup above keeps drafting in a doc the team already edits, but adds a small system that reads that doc every day and posts only what’s approved and due. Posts go out on time, in the right format, to the right channels. The owner stays in control — nothing leaves without approval — and the report afterward means you never wonder whether something actually posted. The scheduler is invisible most of the day; visible only when it posts something or when something needs a fix.

The next four posts walk through each piece in turn: how a post gets drafted, how the schedule fires on time, how a post gets formatted per channel, and how a post gets approved or held. One diagram per post. A cost breakdown and a final engineering reference at the end.

All posts