For a small SaaS with no regulator, I start at 30 days of searchable logs, 90 days in cheap archive, and 12 months for security and audit events. How long to keep application logs also depends on what you keep today: Supabase holds 1 day of logs on Free and 7 days on Pro, a window the plan chose.

How long to keep application logs: a starting window by log type, and what changes it

My default window for application logs at a small SaaS with no regulator is 30 days searchable and 90 days archived, with security and audit events at 12 months and payload logs at 30 days. Three things move the window: a contract or framework that names a number, how late questions arrive, and personal data in logs.

These numbers are where I start, not a standard. The retention section of error logging best practices says there is no universal retention period for a small app , and I agree with it: every window on this page is a starting point you adjust.

Retention is one control in logging and monitoring, the one that decides whether yesterday’s evidence still exists when someone asks for it.

The retention log starts here: one row per kind of log, with the window I’d start from and what moves it. The best practice for log retention, as far as one exists, is to write down how long logs should be retained for each type and why, then check that the setting matches what you wrote.

Log typeWhere I would startWhat lengthens itWhat shortens it
Request and application logs30 days searchable, 90 days archivedCustomer disputes that arrive late; a contract termPersonal data in the lines
Error tracker eventsThe vendor plan’s window: by default, Sentry keeps errors 30 days on Developer and 90 days on Team and Business/Enterprise, and logs 30 days on eachA plan with a longer windowA plan with a shorter window; a plan change applies to new data only
Authentication and security events12 months, because questions about an intrusion can come weeks or months laterA framework that names a number (the checklist below)Rarely worth it: cut fields instead
Audit logs of sensitive actions12 months, or the contract’s termA customer contract that names a longer termA contract term shorter than a year
Webhook and model request payload logs30 daysAn open dispute about one payment or one answerCustomer content in the bodies
Metrics15 days at full resolution, longer when downsampledMonth-over-month comparisonsDisk size
Debug logs7 days, or offAn open investigation (a preservation hold)No investigation open

The error tracker row quotes Sentry’s data retention periods as read on 2026-10-03.

Three things change a window. A contract or framework that names a number comes first, and the checklist further down reads what the common ones actually say. Then the longest gap you have seen between an event and someone asking about it: NIST’s log management guide notes that data about a particular event could be needed weeks or months after it occurred. A billing dispute is one such late question; a post-incident review published to the status page is another (what is a status page, and when a small SaaS needs one covers that page). Personal data in the logs pulls the other way, toward a shorter window; the GDPR line is in the next section.

Setting and documenting log retention across the application’s services is deliverable 8.5 of the Production Hardening Sprint, because retention that is too short impairs investigation and uncontrolled retention increases cost and exposure.

What is a log retention policy

A log retention policy is one page: a register with a row for every place the app keeps logs, giving the window, where it is set, who can read it and how deletion happens, plus three rules on who may read logs, what must never be logged, and who reviews the register and when.

What log retention means is set out in NIST’s guide to computer security log management: log retention is “archiving logs on a regular basis as part of standard operational activities”, while log preservation is “keeping logs that normally would be discarded, because they contain records of activity of particular interest”, typically in support of incident handling or investigations. So I add one more line to a retention policy for logs, for a hold: who can stop deletion of a store while an investigation runs.

The register for one small SaaS, filled in, with ___ where your own values go:

StoreWhat it holdsWindowWhere it is setWho can read itHow deletion happensLast verified
App platform logs (Vercel)Function output and request lines1 day on ProThe Vercel plan; a drain for more___Vercel, at the plan’s window___
Database and auth logs (Supabase)API, Postgres and Auth logs7 days on ProThe Supabase plan; a log drain for more___Supabase, at the plan’s window___
Error tracker (Sentry)Errors and logs30 or 90 days, by planThe Sentry plan___Sentry, at the plan’s window___
CDN or WAF logsEdge requests and blocks___The provider’s dashboard_________
Email provider eventsDeliveries and bouncesTheirs, window per their docsThe provider___The provider___
Audit tableSensitive actions12 monthsA scheduled delete jobAdmins onlyThe job; backups keep copies longer___
VPS log files/var/log/myapp/*.log30 days/etc/logrotate.d/myapproot and the app userlogrotate___
Archive bucket (S3)Logs shipped off the box and from drains90 daysA lifecycle rule___S3 expiration___

User data has its own schedule and triggers, in a data retention policy template for a small SaaS. That template has one row for security and application logs, which this register fills in, and it already puts webhook and LLM request logs at 30 days, the same window as the table above.

What goes wrong without it: logs deleted before the bug was investigated

Missing retention fails in four ways, and only one of them is about losing logs too soon.

SituationWhat happensThe register line that prevents it
Too shortA customer disputes something older than the plan keeps; on Supabase the window is set by plan, from 1 day on Free to 28 on TeamA window chosen on purpose, with a drain where the plan’s window is shorter
UnknownA security questionnaire asks for the log retention period and nobody can answer for every storeOne row per store, with its window and the date it was last checked
UncontrolledRequest logs holding emails and IP addresses, kept for years, widen what any breach exposesA window on every store, and a field list that keeps personal data out
The full diskA server with no rotation keeps appending to /var/log until the disk fillsA logrotate file per app

The full plan table and the log drain price are in Supabase’s plans and what each includes.

The uncontrolled case has a legal edge in the EU. Article 5 of the GDPR says personal data shall be “kept in a form which permits identification of data subjects for no longer than is necessary for the purposes for which the personal data are processed”. The article goes on to allow longer storage only where the data will be processed solely for archiving purposes in the public interest, scientific or historical research purposes or statistical purposes, under the safeguards of Article 89(1). As I read the article, a request log kept for debugging fits none of those. This is not legal advice. A full disk is the other failure I would expect, and rotation is what stops it; the logrotate section below shows the file.

Take a founder whose app runs on Supabase’s Pro plan with no log drain, so the only record of its API and auth requests is the project’s own logs. A customer writes about a failed sign-in from earlier than the plan’s 7-day log retention, and the request is no longer there to read. A log drain, which Supabase offers only on Pro, Team and Enterprise plans, would have sent a copy somewhere the founder controls. The plan chose the window, not the team.

The same gap shows up in my June and July 2026 audits, where 17 of the 21 third-party apps had no error tracking or alerting: when a user hits an error, nothing records it. I chose those 21, public and held-out third-party apps I audited, so the count describes them and is no rate for AI-built apps in general. In an app like that, the host’s default log window is usually the only record there is. Deciding what goes into each line, and what to redact before it is written, is part of how to do logging, a separate job from deciding how long to keep it.

How to set log retention periods, service by service

To set log retention periods, work through five places: the hosted services, any server you own, metrics and audit logs, the frameworks that name a number, and the cost, with sampling to cut it.

Hosted platforms and log services: where the window is set

Each hosted service sets its own window, and three of the five named services below set it by pricing plan. Every cell below comes from the vendor’s docs, read on 2026-10-03; where the docs I read say nothing, the cell says so.

ServiceWhat it keeps by default or by planCan you change itHow to keep longerSource, checked
Supabase1 day on Free, 7 days on Pro, 28 days on TeamBy plan: its logs page says retention is based on the project’s pricing planA log drain, on Pro, Team and Enterprise onlySupabase pricing and log drains docs, 2026-10-03
Vercel runtime logs1 hour on Hobby, 1 day on Pro, 30 days on Pro with Observability Plus, 3 days on Enterprise, 30 days on Enterprise with Observability PlusBy plan, or with Observability PlusDrains, on Pro and EnterpriseVercel runtime logs and drains docs, 2026-10-03
Amazon CloudWatch LogsIndefinitely, by defaultYes, per log group, at any timeIt already keeps everything until you set a windowCloudWatch Logs user guide, 2026-10-03
Google Cloud Logging30 days in the _Default and user-defined buckets, if not customizedYes, from 1 to 3,650 days; the _Required bucket’s period cannot be changedA longer bucket retention; retention costs apply past the defaultCloud Logging buckets docs, 2026-10-03
SentryBy plan, as in the window table aboveBy plan: retention is set when data is ingested, based on the then-current plan; Sentry’s docs point to an upgrade or to support for enterprise retention optionsA plan with a longer window, for errorsSentry data retention periods, 2026-10-03
Payment and email providersTheirs, window per their docsTheirsKeep what you need in your own storeEach provider’s docs

To check the log retention period on your own account, open each row’s console: the Supabase plan page, the Vercel project’s Logs tab, the Retention column on CloudWatch’s log groups, the bucket settings in Cloud Logging. The sources are Supabase’s log drains, Vercel’s runtime logs documentation, CloudWatch Logs retention settings and Cloud Logging buckets.

Vercel’s short windows matter most during an outage, and what to copy off the host while it is down belongs to what to do first when your app is down, so it is not repeated here. On CloudWatch, the opposite risk applies: by default log data is stored indefinitely, so a group nobody configured keeps growing. The rest of AWS logging and monitoring, beyond retention, is a separate setup.

To keep logs longer than a plan allows, I’d use a drain or export to object storage with a lifecycle rule that expires objects at the archive window. With S3 lifecycle rules, expiration actions define when objects expire, and Amazon S3 deletes expired objects on your behalf. Payment and email providers keep their own event logs on their own schedule, so the register lists them as “theirs, window per their docs” and nothing more.

Rotating logs on a box you own: Linux logrotate

Linux logrotate rotates, compresses and removes log files on a schedule, configured per app in a file under /etc/logrotate.d/. Old copies are rotated the rotate count of times before removal, so daily rotation with rotate 30 keeps about 30 days of old files. A dry run with logrotate -d prints debug messages and changes nothing.

What logrotate is used for, in the logrotate manual page: it “allows automatic rotation, compression, removal, and mailing of log files”, and each log file “may be handled daily, weekly, monthly, or when it grows too large”.

To configure logrotate for one app, leave the main file, /etc/logrotate.conf, alone and add a file in /etc/logrotate.d/. The logrotate project’s example config pulls that directory in with include /etc/logrotate.d, under the comment “packages drop log rotation information into this directory”; the manual itself documents the include directive, not the path.

The window arithmetic is mine, from the way the manual defines rotate (its row is in the options table below): the window is roughly the rotate count times the rotation period, so weekly with rotate 4 keeps about 28 days.

The logrotate options that decide a retention window, each “what it does” cell in the manual’s words, shortened:

OptionWhat it doesWhen to use it
daily, weeklydaily: log files are rotated every day; weekly: rotated on the given weekday (Sunday when none is given), or if the date is advanced by at least 7 days since the last rotationdaily for a busy app log, weekly for a quiet one
rotate NLog files are rotated count times before being removed; if count is 0, old versions are removed rather than rotated; default is 0Always: it sets the window
maxage NRemove rotated logs older than N days; the age is only checked if the log file is to be rotatedA hard age cap on top of the count
size, maxsizesize: rotated only if bigger than size bytes, exclusive with the time options; maxsize: rotated when bigger than size bytes even before the time intervalmaxsize on a log that can spike
compress, delaycompressOld versions are compressed with gzip by default; delaycompress postpones compression of the previous log file to the next rotation cyclecompress to save disk; delaycompress is in the FAQ
missingokIf the log file is missing, go on to the next one without issuing an error messageAn app that creates its log only when it first writes
notifemptyDo not rotate the log if it is emptyA log that is quiet some days
dateextArchive old versions adding a date extension like YYYYMMDD instead of a numberFinding one day’s file quickly
create, copytruncatecreate: the log file is created again right after rotation; copytruncate: truncate the original in place after creating a copy, for a program that cannot be told to close its log file; a very small time slice between copying and truncating can lose some logging datacreate when the app reopens its file on a signal, copytruncate when it cannot
postrotate … endscriptThe script is executed after the log file is rotated and before it is compressedTelling the app to reopen its file

A file for a Node.js or Python app that writes to /var/log/myapp/ and cannot be told to reopen its log, saved as /etc/logrotate.d/myapp:

/var/log/myapp/*.log {
    daily
    rotate 30
    maxage 30
    missingok
    notifempty
    compress
    delaycompress
    dateext
    copytruncate
}

Before trusting it, run sudo logrotate -d /etc/logrotate.d/myapp: debug mode means no changes are made to the logs and the state file is not updated. The system journal keeps its own store with its own limits in journald.conf: SystemMaxUse= controls how much disk space the journal may use up at most, and MaxRetentionSec= sets the maximum time to store entries. The manual says time-based deletion should normally not be required, because size-based deletion with options such as SystemMaxUse= should be sufficient, and adds that to enforce data retention policies it might make sense to change MaxRetentionSec= from its default of 0, which turns the feature off. Container logs live in a different file with their own settings: how to clear Docker logs.

Rotation deletes, so an archive window longer than the rotate count only exists if the logs leave the box before rotation removes them; I ship them to object storage daily.

Retention beyond application logs: metrics and audit logs

Prometheus keeps local data for 15 days by default when neither retention flag is set, and --storage.tsdb.retention.time changes it. An audit log retention policy runs longer than the application window, with 12 months as my starting point or the contract’s term, because questions about who changed a role or exported data arrive months later.

How long Prometheus should retain data is set by two flags in Prometheus’s storage documentation. --storage.tsdb.retention.time sets how long to retain samples, --storage.tsdb.retention.size caps the bytes of storage blocks kept, and if neither is set the retention time defaults to 15d. When you use the size cap, the docs recommend setting it to at most 80-85% of the disk allocated to Prometheus. Prometheus’s local storage is limited to a single node’s scalability and durability, so Prometheus offers interfaces to remote storage systems; the docs add that, with proper architecture, years of data can be kept in local storage. What each metric type records is a separate subject: Prometheus metric types.

How long audit logs should be retained depends on who asks the questions, and they ask late: who changed this role, who exported that data. Where a customer’s contract names a term, that term wins; otherwise I aim for a year. Where the audit log is a table in the app’s own database, its window belongs in the user-data schedule as well, and backups hold copies past it; the backups section of the retention template covers that part. What the audit log should record in the first place is the subject of audit logging best practices for sensitive actions.

A log retention compliance checklist: what the common frameworks actually say

A log retention compliance checklist separates hard numbers from principles. The EU GDPR names no period: personal data stays identifiable no longer than its purposes need, which I take as a case for shorter log windows. HIPAA’s rule for covered entities and business associates keeps required documentation 6 years; whether that reaches audit logs is a question for counsel.

Each row with a source was read from the primary text on 2026-10-03, and each says whether the rule is required or good practice.

FrameworkWhere it appliesWhat it says about log retentionHard numberRequired or good practiceSource
EU GDPR, Article 5(1)(e)Personal data under the GDPRStorage limitation: identifiable no longer than necessary for the purposes (quoted in the section above)No number, a principleRequired where the GDPR applieslegislation.gov.uk, text as adopted
HIPAA, 45 CFR 164.316(b)(2)(i)US covered entities and business associatesRetain the documentation required by paragraph (b)(1) for 6 years from its creation or the date it was last in effect, whichever is later; that section does not say whether audit logs count6 years, for that documentationRequired where HIPAA appliesGovInfo, 2024 CFR
NIST SP 800-92 (September 2006)US federal guidance, not a legal requirement for a private SaaSPolicies should define how long each type of log data must or should be preservedNo numberGood practiceNIST CSRC
PCI DSS v4.0.1, Requirement 10.5.1Businesses whose self-assessment questionnaire includes it: SAQ D and SAQ A-EP print it, SAQ A does not”Retain audit log history for at least 12 months, with at least the most recent three months immediately available for analysis”12 months, 3 immediately availableRequired where in scopePCI council’s SAQ files
ISO/IEC 27001, SOC 2Companies seeking that certificate or reportNo figure quoted here; I would let your own written policy set the numberNot quoted hereYour policyNot quoted

The texts behind the table are 45 CFR 164.316 and the PCI council’s self-assessment questionnaire D, in its v4.0 edition; v4.0 was retired on December 31, 2024, and v4.0.1 keeps the 10.5.1 text unchanged. NIST and the GDPR are linked above. My five-line checklist, in order:

  1. 01 List the frameworks that actually apply to the app, from contracts and questionnaires, not from a blog list.
  2. 02 Write the number each one requires next to it, or "no number" where it states a principle.
  3. 03 Set each log type's window to the longest that applies to it.
  4. 04 Keep personal data out of the logs, so a long window is safe to hold.
  5. 05 Keep the evidence of each configured window, with the checks in the next section.

Log sampling and the cost of keeping more

Log sampling keeps a fraction of repetitive events, such as the 1 in 10 health checks I’d start with, and keeps every event that matters: errors, authentication, security, audit and billing. Sampling by request id keeps a request’s lines together, and recording the rate on each event lets counts be scaled back.

Keeping logs costs money twice on a hosted log service: CloudWatch pricing lists a charge to collect (data ingestion) per GB and a charge to store (archival) per GB compressed. The table works through my assumption of one app writing 2 GB of logs a day, priced once in CloudWatch Logs and once as plain files in S3 Standard in the same region.

WindowLogs held (uncompressed)CloudWatch Logs Standard a month: collect + storeS3 Standard a month, uncompressed files
30 days60 GB$30.00 + $0.27 = $30.27$1.38
90 days180 GB$30.00 + $0.81 = $30.81$4.14
365 days730 GB$30.00 + $3.29 = $33.29$16.79

Prices checked 2026-10-03, US East (Ohio): CloudWatch Logs Standard at $0.50 per GB to collect and $0.03 per GB compressed to store, with the pricing page’s 0.15 compression ratio for each uncompressed byte; S3 pricing for S3 Standard at $0.023 per GB for the first 50 TB a month. The free tier, request charges and data transfer are left out.

On CloudWatch, collection is the bill: keeping a year instead of a month adds about $3 a month at this volume, while every GB you never send saves $0.50. That makes volume the lever, which is where sampling comes in.

For sampling, these are the steps I take, in order:

  • Cut volume at the source first: raise the log level in production and drop noisy fields before you sample anything.
  • Never sample errors, warnings, authentication and security events, audit events or billing events.
  • Sample the repetitive and boring: health checks, successful static requests, debug lines, at a rate you write down; I’d start at 1 in 10.
  • Sample by request id, not by line, so a kept request is kept whole.
  • Record the sample rate on each kept event, so a count of 120 kept events at 1 in 10 reads as about 1,200.

How to verify it: verify the configured log retention window

A log retention window is verified in 6 checks: list every store, read each configured window, find each store’s oldest event and compare its age with the window, dry-run logrotate on servers, restore one archived day and search it, and send marked test values in the banned fields and confirm no store kept them.

  1. 01 List every log store the app writes to, one register row each. Pass: every store has a row, including the one nobody thought of. Evidence: the dated register.
  2. 02 Read each store's configured window from its console or CLI and record it. On CloudWatch, aws logs describe-log-groups returns retentionInDays, the number of days to retain each group's log events; AWS's CLI reference does not say how a group that never expires shows there, so check any group without a number in the console's Retention column, where Never Expire means no window. Pass: every row carries a window read today. Evidence: the CLI output or a screenshot per store.
  3. 03 Check behavior, not only settings: search each store for its oldest event. Pass: on a store with steady traffic its age is about the window, neither younger (you keep less than you think, unless the store is newer than one window) nor far older (deletion is not happening). CloudWatch typically takes up to 72 hours after the retention setting to delete events, and in rare situations it might take longer, so an oldest event up to three days past the window is not yet a failure there. Evidence: the oldest timestamp per store.
  4. 04 On a server, run logrotate -d on the app's config, then list the rotated files with their dates. Pass: no more rotated files than the rotate count, and none older than about the window. Fewer files is normal on a box set up less than one window ago or where notifempty skipped empty days, and maxage is only checked when a rotation happens, so a file a little past the window after a quiet stretch is not yet a failure. Evidence: the listing.
  5. 05 Where the register has an archive, a drain or export to object storage (Supabase and Vercel offer drains on Pro and above), fetch one archived day and search it for a request id your app logged in that same store on that day. Pass: found, within about an hour. Evidence: the query and its result.
  6. 06 On staging, send one request that carries a marked test value in each field the policy bans, such as a test email address and a fake token, then search every store for those exact values and for the request's own id. Pass: the request id is found and the marked values are not. Evidence: the request, the searches and their results.

The request id in the last check matters: a search for values nobody sent passes on any build, and a store that never logged the request passes too, so finding the id is what makes the empty result mean something. Log volume per day and the oldest event’s age per store are two numbers worth a dashboard tile, part of how to build an ops dashboard. Repeat the checks when a plan, a host or a log service changes.

On the sprint, deliverable 8.5 is verified this way: inspect the configured windows and verify retention behavior in supported systems. Deliverable 8.1 is verified by tracing a test request across services and checking log content for sensitive fields.

Where the sprint does this

The log retention work named in the first section, with its reason, is deliverable 8.5. Deliverable 12.5 in the published scope documents retention periods and handling rules for logs, user history, and abandoned records. The production readiness report, deliverable 13.1, is verified this way: account for all 123 IDs; keep failures visible until resolved and explain genuine non-applicable items. Legal advice and certification are separate services; the sprint implements and documents the technical data-handling controls. Hosting, paid tools and API usage remain in the client’s accounts.

Common questions about log retention

What is the 7 year retention policy?

The 7-year retention figure is usually US tax record keeping, not a rule for application logs: none of the frameworks quoted above sets 7 years for them. For tax records, the IRS says to keep records 3 years if none of its listed exceptions apply, and 7 years if you file a claim for a loss from worthless securities or bad debt deduction. HIPAA’s documentation rule, in the frameworks table, is 6 years.

Does logrotate run automatically?

Yes: normally logrotate is run as a daily cron job, or by logrotate.timer on systems that use systemd, and it only rotates the files its configs name. It will not modify a log more than once in one day unless that log rotates by size and logrotate runs more often, or unless the -f option is used.

How to run logrotate manually?

Run sudo logrotate -f /etc/logrotate.d/myapp to force a rotation now, even if logrotate doesn’t think one is necessary. Use -d instead to see what it would do in debug mode, where no changes are made to the logs. Run it as root or with sudo, as above.

What is the purpose of the delaycompress option in logrotate?

It postpones compression of the previous log file to the next rotation cycle, so the newest rotated file stays uncompressed for one more cycle. It only has effect together with compress, and the manual says it can be used when a program cannot be told to close its log file and might keep writing to the previous file for some time.