Español: README.es.md
A free script that prints one tiny maintenance page a month so your inkjet printer never dries up from sitting idle — and skips the page completely if you already printed something on your own. No installs, no admin rights, no dependencies.
It speaks your language automatically: if your system is set to Spanish, the interface and the printed page switch to Spanish for you; everyone else gets English.
Your printer must already work on your computer. If you can print a document today, you are ready.
This tool does not install or configure printers — it just uses the one you already have, the same way your web browser does. Chrome doesn't install drivers or set up your printer before printing a page; it just sends the job to whatever printer your computer already has ready. Printer KeepAlive works the same way:
- It does not install any printer driver or change your printer's settings
- It does not need your printer's IP address — that's optional everywhere,
and only used to show ink levels on HP printers. On Windows you can leave
it empty and the tool tries to work the address out on its own; macOS and
Linux don't try, so there you add it by hand in
config.jsonif you want that detail. See Configuration below — including why Windows 11 users sometimes need to set it by hand too - It works with any printer that already prints from your computer: USB, WiFi, network, or shared
- It does not need administrator rights
- It does not install anything system-wide — it's just a few files in whatever folder you put them in
One note for Windows: the ink level block and the update check are done with
curl.exe, which ships with Windows 10 version 1803 and later. On an older
Windows everything else still works — the page prints on schedule — you just
won't see the ink block, and the tool won't tell you when a new version is
out.
Being upfront about this matters more than sounding more tested than we are:
- Built and tested on an HP DeskJet 2820e, on Windows 11. That's the printer and machine this whole project started with.
- The parts that talk to the operating system — printing the page, detecting printers, scheduling the daily check, counting pages — use standard OS features, not anything HP-specific. They should work with any brand: Canon, Epson, Brother, and others.
- The one HP-only part is the ink levels and warnings block. That comes from HP's own web API on the printer. With another brand, that block simply doesn't show up — everything else keeps working exactly the same.
- The macOS and Linux scripts are written to follow the same logic as the Windows one, but they have not been tested on real macOS or Linux hardware yet. If you try one of them, we'd genuinely like to hear how it went — see Contributing below.
Inkjet nozzles dry out from not being used, not from being used.
On many home printers (HP 305 cartridges, Canon PG-xxx, and others) the print head is built right into the ink cartridge. That matters: when a cartridge dries up, you are not cleaning a head — you are throwing the whole cartridge away.
This project started with a real case, August 2026, an HP DeskJet 2820e: the printer sat unused for six weeks. The next print came out blank, then pale grey. The black cartridge was ruined. Nothing was broken — the ink had simply dried inside the nozzles. The cartridge still showed 10% ink left. It went in the bin anyway.
If you have ever searched for prevent printer ink from drying, inkjet nozzle clog, printer not printing after not using it, or dried ink cartridge, this is built for exactly that problem.
Printer KeepAlive runs quietly in the background and prints a small test page every so often — just enough ink flow to keep every nozzle wet. If you already used the printer recently, it skips the page, because your own printing already did the job.
Think of it as a printer maintenance schedule that runs itself, and only steps in when you haven't printed anything on your own.
This is not the only tool for the job, and other people have shared their own solutions. Two open-source ones worth a look:
- printer-ink-dry-up — a bash script that, according to its README, prints a test page from a cron job "to prevent cartridges from drying out".
- epson-color-keep-alive — described by its author as a script that makes an Epson printer print a test pattern every week so the nozzles don't get gummed up.
If one of those fits your setup better, use it. Printer KeepAlive was written because we wanted three specific things for our own case:
- Skip the page when you already printed. Any printing you do yourself already keeps the ink moving, so an extra maintenance page on top of it is wasted paper and ink. This script looks at the printer's page counter and ink levels first, and only prints when nothing else has.
- A page shaped around how nozzles are laid out. Thin vertical lines instead of solid colour, so every nozzle fires while very little ink reaches the paper. The reasoning is in How it works inside.
- Show the real printer status when the printer can report it.
These numbers are our own, from the printer this project was built on — an HP DeskJet 2820e. They are what we measured on that machine, not a general law and not a claim about anyone else's tool.
Two test pages filled with solid colour blocks used 5% of the colour cartridge each. The same maintenance job drawn with thin vertical lines uses under 1% per page. At 13 pages a year, that is roughly 65% of a colour cartridge spent on upkeep versus about 10%. Another printer, or other cartridges, may well give different numbers.
No Python, no Node.js, no Homebrew, no admin or root password. The script only uses tools that already ship with your operating system.
- Download the project (green Code button on GitHub → Download ZIP,
then extract it anywhere, e.g.
Documents\printer-keepalive). - Open the
windowsfolder. - Double-click
Printer KeepAlive.cmd.- It shows a short "please wait" message first, since starting PowerShell takes a moment. The window opens right after.
- If Windows shows a blue "Windows protected your PC" warning, click More info → Run anyway. This is a normal Windows safety check for files downloaded from the internet, not a problem with the script.
- A window opens. Click Enable to turn on automatic maintenance. No admin rights are needed — the schedule is created for your own user account only.
- Download the project (green Code button on GitHub → Download ZIP,
or
git cloneit). - Open Terminal, go to the
unixfolder, and run this once — it's the only Terminal step you'll ever need:cd ~/Downloads/printer-keepalive/unix chmod +x "Printer KeepAlive.command"
- In Finder, double-click
Printer KeepAlive.command. It opens its own Terminal window with the menu inside.- The first time, macOS may refuse to open it because the file was downloaded from the internet ("cannot be opened because it is from an unidentified developer"). Control-click (or right-click) the file, choose Open, and confirm Open in the dialog that appears; if that option isn't offered, go to System Settings → Privacy & Security and click Open Anyway.
- Choose Enable to turn on automatic maintenance.
From now on, just double-click Printer KeepAlive.command again whenever
you want to open the tool — no more Terminal needed.
- Download the project the same way as above.
- Open a terminal and go to the
unixfolder. - Run:
chmod +x printer-keepalive.sh ./printer-keepalive.sh
- If
zenityis installed you get a graphical menu; otherwise a text menu in the terminal. Choose Enable to turn on automatic maintenance.
On Windows, if this computer already has an older printer maintenance script scheduled (for example, the original version this project grew out of), Printer KeepAlive notices it the first time it runs and offers to turn it off for you. This avoids ending up with two maintenance pages printing instead of one. It always asks before doing anything, and it never deletes files on its own.
If you say yes, it also copies over the date of the last page the old install printed. That way the countdown carries on where it left off, instead of printing a page you don't need right after the switch. This only happens when this install has no history of its own yet — a real date here is never overwritten.
Running the script with no arguments always opens the interface. From there you can see, at a glance:
- Whether automatic maintenance is ON or OFF
- Which printer(s) it is watching
- How many days since the last maintenance page, and how many until the next
- The printer's status and ink levels, when it can report them — each cartridge shows as a bar with its percentage; the percentage is colored green for fine, amber for getting low, and red for very low or empty
- Whether a newer version is available
On Windows, the window stays open after every click — it updates itself in place instead of closing, so you never have to reopen it between actions. A Refresh link inside the status box forces a fresh check of the printer, in case something changed since the window opened.
Also on Windows, a language selector sits at the top right of the window,
with three options: Automatic, English, and Spanish. Changing it saves to
config.json right away and redraws the window in the new language — no
restart needed. macOS and Linux have no such selector; there, language in
config.json is the only way to change it (see Configuration).
And you can:
- Enable automatic maintenance
- Disable it
- Print a page now, on demand
- Choose printer(s) from the ones detected on your system
- Update (only shown when a newer version exists)
- Remove from this computer — for example before giving away or retiring a machine
- Postpone the failure alert — silence the "could not print" reminder for a while
- Quit
The text menu uses these same numbers on all three systems.
On Windows, printing a page on demand (Print a page now) shows you right away if it failed, with the reason why — it no longer just says "Done" when nothing was actually printed. (macOS and Linux already worked this way.)
For the automatic daily check, an alert appears when either of two things happens, whichever comes first:
- The same failure happens three days in a row. One bad day is normal — a printer that was off on Saturday just prints again on Monday, and the retry happens on its own. But the same problem three days running is not bad luck, so you get told about it quickly instead of possibly waiting weeks in silence.
- More days have gone by than
alertAfterDayssince anything last reached the printer (your own printing counts too). This catches the slower version of the same risk: a printer that has gone quietly unused for a long time, even if maintenance was only attempted a couple of times.
This dual trigger is currently a Windows-only improvement. On macOS and
Linux the alert still only watches alertAfterDays — there is no separate
"three failures in a row" trigger there yet.
What the alert says. On Windows, the alert names the actual cause instead of just saying "could not print": printer not found, off or asleep, out of paper, a paper jam, a cover or door open, an empty cartridge, printing paused in Windows, a general printer error, or a Windows spooler problem. Each cause comes with a concrete suggestion — "put paper in the tray", "switch the printer on", and so on. When the real cause can't be worked out from what Windows and the printer reported, it says so plainly instead of guessing. macOS and Linux do not yet do this diagnosis: their alert just says a page could not be printed, without naming a specific cause.
Copying or reporting the problem. The Windows alert also has two buttons:
Copy report puts a ready-to-paste Markdown report on your clipboard
(Windows and PowerShell version, printer name/driver/connection, the
diagnosis, the raw technical detail, and the last lines of history.log),
and Report this problem does the same and then opens the page for
creating a new GitHub issue, so you just paste with Ctrl+V. Before anything
is copied, the report is scrubbed automatically: IP addresses, any file path
containing your Windows username, your account name and your computer name
are all removed. That's not just a privacy detail — it's why you can use
these buttons without having to check the text yourself first. These two
buttons, and the scrubbing, are Windows-only for now; macOS and Linux don't
have them.
The repository also has ready-made issue forms for reporting a problem by hand — see Contributing below.
Whichever platform, postponing never turns maintenance off: the daily check keeps running, and as soon as a page prints successfully the postponement is cleared on its own.
You can postpone for 3 days, 1 week, 1 month or 3 months. 1 week is the option you get by default, and these are the same four choices on Windows, macOS and Linux.
You don't have to wait for the alert either. Postponing is also option 7 in the menu — a button in the Windows window, an entry in the macOS and Linux menus, a numbered option in the text menu. That matters when the alert never showed up, for example because the daily check ran with no desktop session open.
When no dialog can be drawn at all (nobody logged in, or zenity missing on
Linux), no alert appears and the failure is only written to history.log.
Opening the tool by hand still gives you the menu, with the same postpone
options.
The date until which the reminder stays quiet is kept in snooze.txt, next to
the script.
Almost everything works the same everywhere: the daily check, when a page is due, the page itself, the settings file, the log. These are the points where the systems really differ:
| Windows | macOS | Linux | |
|---|---|---|---|
| Interface | window | system dialogs | zenity if installed, otherwise a text menu |
| Language selector | in the window, top right — changes it instantly | none — edit language in config.json |
none — edit language in config.json |
| Double-click launcher | Printer KeepAlive.cmd |
Printer KeepAlive.command |
none — run ./printer-keepalive.sh from a terminal |
| How the daily check is scheduled | Task Scheduler, under your own user | a launchd agent in your own account | a systemd user timer, or your own crontab if the machine has no systemd. With cron, Disable removes the entry, because cron has no "off" state |
| Printer address for ink levels | found on its own, or set printerIP in config.json (needed on Windows 11 for auto-discovered/WSD printers — see Configuration) |
set printerIP in config.json |
set printerIP in config.json |
| What the status block shows | printer state, ink levels and HP warnings | print queue state and ink levels | print queue state and ink levels |
| Older install detected | yes, it offers to disable it | not applicable | not applicable |
| Failure alert trigger | 3 failures in a row, or past alertAfterDays |
past alertAfterDays only |
past alertAfterDays only |
| Failure alert detail | names the cause (out of paper, jam, offline...) with a suggestion; can copy a report or open a GitHub issue with it, IPs/paths/names removed automatically | generic "could not print" message, no cause named | generic "could not print" message, no cause named |
| Ink block and update check need | curl.exe (Windows 10 1803+) |
curl, ships with macOS |
curl, usually installed |
Settings live in config.json, next to the script. It is created the first
time you run the script and is never overwritten by updates — your
settings are always safe.
{
"printers": [],
"daysBetweenPages": 28,
"checkTime": "16:00",
"alertAfterDays": 45,
"checkForUpdates": true,
"printerIP": "",
"language": "auto"
}| Option | Type | Default | What it does |
|---|---|---|---|
printers |
list of names | auto-detect | Printers to maintain. Leave empty to auto-detect your default printer. |
daysBetweenPages |
number (days) | 28 |
How long the printer can sit unused before a maintenance page prints. |
checkTime |
"HH:MM" |
"16:00" |
What time of day the daily check runs. |
alertAfterDays |
number (days) | 45 |
If maintenance keeps failing (e.g. printer stays offline) past this many days, you get an alert. |
checkForUpdates |
true / false |
true |
Set to false to stop the script from ever contacting the network. |
language |
"auto" / "en" / "es" |
"auto" |
Interface and printed page language. auto follows your system's language. Set en or es to force one, no matter what your system uses. On Windows you can also change this from the language selector in the window (top right) instead of editing the file — it saves the moment you pick one. macOS and Linux have no menu for it; edit this file directly. |
printerIP |
text | empty | Your printer's IP address (e.g. "192.168.1.50"), used to read ink levels and status from HP printers. Optional everywhere. On Windows this setting is read first; if it is empty, the tool tries to find the address itself, and if it can't, it says so on screen and explains how to add it. That last case is normal on Windows 11 when the printer was added by automatic discovery (it shows up as a "WSD" port), because that kind of connection publishes no address at all; setting it by hand also starts the check noticeably faster. On macOS and Linux there is no automatic detection, so set it by hand there if you want ink levels. There is no menu for this setting on any platform — edit config.json directly. Printing itself works exactly the same with or without it. |
Not sure what your printer's IP address is? Check the printer's own screen,
or print a network status page from the printer's own menu — most printers
can show or print this themselves. On Windows, if no address can be found
either way, the tool explains this on screen and notes it in history.log,
rather than just leaving the ink block out without saying why.
A few more files live alongside the script and you never need to edit them by hand:
state.json— remembers ink levels, print counts, and dates, so the script knows what changed since yesterday.history.log— one line per action taken, for troubleshooting. This log is always written in English, on purpose — so that whoever helps you troubleshoot a problem, in any country, can read it.snooze.txt— only appears if you postpone the reminder. It holds the date until which it stays quiet. Delete it and reminders come back.
Why nozzles dry out from not printing, not from printing. Each nozzle holds a tiny drop of ink at its tip. Ink is designed to dry when exposed to air — that's how it stays put on paper. Leave a nozzle idle for weeks and that same drying happens right at the tip, sealing it shut. Using the printer regularly keeps ink moving through and replaces it before it can dry.
Why a dry cartridge on many printers means the whole cartridge is scrap. On printers like the HP DeskJet 2820e, the print head isn't a separate, reusable part of the printer — it's built into the cartridge itself. So a clogged nozzle isn't a $20 cleaning job, it's a "buy a new cartridge" problem, even if that cartridge still has ink left.
Why thin vertical lines, not blocks. Inside the print head, the nozzles sit in a vertical column, and the carriage carrying that head moves left to right across the page. A vertical line makes the carriage pass every nozzle in the column while barely touching the page — full exercise, minimal ink. A horizontal line only fires the nozzles at that one height, missing the rest. A solid block fires the same nozzles as a vertical line, just far more often, wasting ink for no extra benefit. That's why the maintenance page uses columns of thin vertical lines, one row per ink colour, spanning the full width of the page — a clogged nozzle shows up as a visible gap in the pattern.
Why colour rows are lighter than the black row. The black cartridge is usually sold on its own and is cheap to replace. The colour cartridge is typically one part holding cyan, magenta and yellow together, and costs more. So the colour rows are printed at half the line density of the black row — enough to keep every nozzle wet, without spending extra ink on the more expensive cartridge.
Why it checks every day instead of every 28 days. If the check only ran once every 28 days and the printer happened to be off or asleep that day, the next attempt wouldn't come for another 28 days — long enough for real damage. Running the check daily means a missed day just tries again tomorrow. The script never asks the printer for the date; it works it out by comparing today's print counter and ink levels against yesterday's.
Why it skips printing if you already printed. Ink flowing through the nozzles for any reason — your own document, a photo, a form — does the same job as the maintenance page. The script checks the printer's own page counter and ink levels first. If either shows real use since the last check, it resets the countdown and skips the maintenance page, because printing one would be pure waste.
Why it never prints to an offline printer. A job sent to a printer that's turned off just sits stuck in the queue. The script checks first, skips the job, logs it, and tries again tomorrow.
Running it twice does nothing twice. If you click Enable when it is already enabled, nothing breaks and nothing gets duplicated — it just replaces the existing schedule instead of creating a second one. If a maintenance page was already printed today, running the script again will not print another one. You can open the script and click around as much as you like; it never repeats an action it has already done today.
Will this work with my printer? It should work with any printer visible to your operating system's normal print system. Ink level and detailed status reporting is best on HP printers; on other brands the page still prints on schedule, just without the extra status details.
Does it need Python, Node.js, or any other software installed? No. It only uses what already ships with Windows, macOS, or Linux.
Does it need admin or root access? No. The scheduled task runs under your own user account.
Will it print if my printer is turned off? No. It checks first, skips the job so it doesn't get stuck in the queue, and tries again the next day.
Will it print if I already printed something today? No. Any real print job — spooler activity or an ink level drop — resets the countdown, so the maintenance page is skipped.
What does the maintenance page actually look like? A title with the date, printer status if available, four rows of thin vertical lines (one per colour: black, cyan, magenta, yellow), and a short note explaining what a gap in the lines means.
Can it install or run anything by itself? No. It only ever checks a version number online. If a newer version exists, it just tells you — nothing is downloaded or run until you click Update yourself.
Will updating erase my settings or history?
No. Updating only ever replaces the script file itself. Your config.json,
state.json, and history.log are never touched, so your settings and
history stay exactly as they were. Before the old script is replaced, it is
also backed up as <name>.backup, so you always have the previous version
on hand.
How do I stop it? Open the script with no arguments and click Disable. To remove it from a machine entirely (for example before retiring it), use Remove from this computer. If you only want the "could not print" reminders to stop for a while, postpone them instead — from the alert itself, or from option 7 in the menu. See When it can't print.
Issues and pull requests are welcome. This started as a fix for one person's printer — the goal now is that it helps the next person who runs into the same problem, on whatever printer they own.
What's most useful right now:
- You have a printer from another brand and something doesn't work? Tell us about it, or send a fix. That's exactly the kind of report that makes this tool better for the next person.
A few things any contribution should keep true:
- Running it twice must give the same result as running it once. Enabling an already-enabled schedule, or checking a printer that was already checked today, should never break anything or do the same thing twice over.
- Never touch the user's printer settings. This tool only uses the printer the operating system already has ready, the same way a web browser does — it must never install drivers or change printer configuration.
- Prefer changes that help more people over changes that only fix one very specific case.
- If you add support for another printer brand (ink levels, status, etc.), make it optional and fail gracefully. If that brand's info isn't available, the tool must keep working normally without it — never crash or block because of it.
- Comments should explain why, not just what — especially around the vertical-line page design and the daily-check logic, so someone adapting this to a different printer can follow the reasoning without running it.
- No external dependencies. Only what ships with the OS.
- Every destructive action should ask for confirmation first.
- Windows, macOS and Linux behavior must stay identical — check
windows/printer-keepalive.ps1andunix/printer-keepalive.shmatch in what they do, even if the code looks different. Where a system genuinely will not allow it (how the daily check gets scheduled is the current example), the difference belongs in Differences between systems so nobody has to find out by using it.
The repository has ready-made issue forms under
.github/ISSUE_TEMPLATE: one for
a printer that does not work,
one for a general bug. Opening a new
issue on GitHub offers these automatically; a blank issue is also fine if
neither one fits.
On Windows, the fastest way to fill one in is the failure alert's Report this problem button (see When it can't print above) — it copies a ready-made report and opens the new-issue page for you. Otherwise, include:
- Your printer's brand and model
- Your operating system (and version)
- What you expected to happen, and what happened instead
- The last few lines of
history.log - On Windows, the Copy report output if you have it
That's usually enough to track down the problem.
MIT — see LICENSE. Copyright (c) 2026 Pedro / Zoroboak.