Changelog

What’s new in Pushify

Every release across the platform, API and dashboard — features, fixes and improvements as they ship.

v0.2.0-beta.109

Dashboard

Fixed

  • Projects list shows suspended projects as suspended. A project suspended under the Acceptable Use Policy looked like an ordinary paused one: yellow "paused" badge, and a Resume item that only failed with an error. It now has a red dot and a Suspended badge (with a hint to open the project for details), and the menu shows a disabled "Suspended" entry instead of Resume. The project page already shows the reason and the appeal address (beta.106).

v0.2.0-beta.108

Dashboard

Changed

  • The reports and appeals address shown on /abuse, /acceptable-use and a suspended project's notice comes from NEXT_PUBLIC_ABUSE_CONTACT_EMAIL (default [email protected]), defined once in lib/contact.ts. Some mail hosts reserve abuse@, so it can now be any address you can receive. Keep it equal to the backend's ABUSE_CONTACT_EMAIL (backend beta.102). It is read at build time, so rebuild after changing it.

v0.2.0-beta.107

Dashboard

Added

  • Acceptable Use Policy at /acceptable-use (English, approved 2026-10-10): - §1: what is prohibited everywhere; - §2: what is additionally prohibited on managed servers, shared infrastructure and *.pushify.dev addresses (§2.1 proxy/VPN/tunnel/relay services, §2.2 network and port scanning). Legal use on your own server with your own domain is not affected; - §3: how it is enforced: no traffic contents or environment values are read, and a person reviews every case before suspension; - §4–6: reporting, appeals and changes. The section numbers match the clauses cited in suspension emails.
  • Report abuse at /abuse: a form (URL, email, category, details; honeypot) that feeds the admin review queue (backend beta.101), plus [email protected].
  • Footer, sitemap and the legal documents bar link both pages.

Changed

  • Terms §9 Prohibited Use (English): it now references the Acceptable Use Policy and adds the proxy/VPN/tunnel/relay and scanning ban on Pushify infrastructure and *.pushify.dev addresses. A violation may suspend the affected project or the account, with or without prior notice. Last updated: October 10, 2026. The Turkish version is unchanged for now (with the lawyer).

v0.2.0-beta.106

Dashboard

Added

  • Admin → Abuse: the Acceptable Use review queue (backend beta.101). Flags are listed by score, with source (deploy scan, runtime, report), reasons (strong/medium/weak, file and line) and report text, filtered by open, actioned, dismissed or all. Actions: - Suspend: a reason sent to the owner, a policy section, and a duration (until appeal, 7, 30 or 90 days); - Lift suspension; - Dismiss, with an optional note. The forms open inline; there are no browser dialogs.
  • Suspended projects show a notice on their page with the reason and the appeal address, and Settings no longer offers "Resume" for them.
  • Self-hosting docs list ABUSE_DETECTION_ENABLED / ABUSE_AUTO_SUSPEND, both off by default.

v0.2.0-beta.107

Platform & API

Fixed

  • Flaky tests. The tests against a real Postgres (*.db.test.ts) share one database, and the deletion purge sweeps every organization that is due, so another file's rows sometimes showed up in its counts and it failed at random. vitest.workspace.ts now runs those files one at a time; unit tests still run in parallel. npm run test -- --run (CI) picks this up unchanged.
  • Lint is clean (eslint src, 0 errors): - unused imports removed from sso.service.ts; - the resolver filter in runner-isolation.ts now sends the intended ^127\. to the shell. Same effect for IPv4 nameservers.

v0.2.0-beta.106

Platform & API

Security

  • Further isolation between projects on shared runners: container log collection, the metrics container lookup and PR-preview teardown now use a project's exact container names as well. Details will be published after rollout.

v0.2.0-beta.105

Platform & API

Security

  • Tighter isolation between projects on shared runners: container, volume and site operations (web shell, delete, pause, resume) now act on a project's exact resources only, and projects whose names collide on a shared runner can no longer be deployed or opened in a shell there. Details will be published after rollout.

v0.2.0-beta.104

Platform & API

Fixed

  • Container metrics and logs of projects on shared runners. Projects without a server of their own (free projects on *.pushify.dev) run on a shared runner. The metrics worker grouped them under "local" and looked for their containers on the control plane. It didn't find them, so every 15 seconds it recorded them as stopped, with CPU, memory and traffic at 0. The container log collector did the same and stored no runtime logs for them. Both now use the runner the deploy used (resolveProjectServerId / pickRunnerServerId). - Side effects of the zeros, now gone: auto-sleep could judge such a project idle however busy it was, autoscale saw 0 % CPU, and Acceptable Use egress fell back to nginx bytes only.

v0.2.0-beta.103

Platform & API

Fixed

  • Pausing, suspending or deleting a project on a shared runner left its containers running. resolveDeployServerForCleanup searched only the customer organization's servers, and the runner belongs to Pushify, so nothing was found. Pause then fell back to the control plane's local Docker, stopped nothing and still reported success. It now resolves a project without a server to the runner the deploy used (pickRunnerServerId). Pause checks that none of the project's containers is still running and reports false otherwise. In production, a project with no server found is reported as not stopped instead of "paused". A suspension now shows the real result ("containers could not be reached") in the admin panel.
  • Suspensions are enforced continuously. Every 5 minutes the containers of every suspended project are stopped again. This catches a pause that failed at the time, a container started by hand, and projects suspended before this fix. It runs whether or not Acceptable Use detection is on.

Note

  • Projects on shared runners that were paused (by their owner, for non-payment, or for account deletion) or deleted before this release may still have running containers on the runner. Suspended ones are stopped automatically within 5 minutes of deploying this.

v0.2.0-beta.102

Platform & API

Fixed

  • npm run abuse:scan -- --enqueue now exits when it is done. The operator emails it queues opened a Redis connection that kept the process running; the flags had already been written.

Changed

  • The appeals address is no longer hard-coded: the "project is suspended" API error and the report rate-limit message use ABUSE_CONTACT_EMAIL, as the suspension email already did. Some mail hosts reserve abuse@; set the variable to the address you can receive (and the frontend's NEXT_PUBLIC_ABUSE_CONTACT_EMAIL to the same).

v0.2.0-beta.101

Platform & API

Added

  • Acceptable Use enforcement for the hosted service — off unless ABUSE_DETECTION_ENABLED=true, so self-hosted installs carry none of it. Applies to projects on managed servers, on shared runners, or reachable on the platform subdomain; a project on its own server with only its own domains is not covered. Needs migration 0071 (abuse_flags, abuse_actions, projects.suspended_*, two activity_action values; additive). - Deploy-time scan. After the clone, the source is checked against config/abuse-rules.yaml; after a successful build, the build log is too. Image deploys are matched by their image reference. The rules cover proxy/VPN/tunnel software (Xray, V2Ray, sing-box, Trojan, Shadowsocks, Hysteria, MTProxy, GOST, Tor relays), known proxy panels, share-link and inbound-config patterns, open proxies and network/SNI scanners. The result is a score with reasons. One strong signal, or two different medium ones, is needed to flag; weak signals (WebSockets, the word "proxy") never flag on their own. Environment files, keys and lockfiles are never read, and reasons record only the rule, file and line — not the matched text. Nothing is written to the customer's deploy log, and a scan can never fail a deploy. - Runtime signals every 5 minutes: 24-hour egress (container counters and nginx bytes sent), the relay pattern (in ≈ out at high volume) and sustained long-lived connections (ss counts per container — counts only). Each is a medium signal. - Review queue for platform admins: GET /admin/abuse/flags, dismiss, suspend (reason, policy clause, optional duration) and unsuspend. Findings are queued and admins are emailed (abuse.flagged). Nothing is stopped automatically unless ABUSE_AUTO_SUSPEND=true (default off), and then only on a strong, high-scoring deploy scan. - Suspension reuses the pause: containers stop, the project is marked suspended, and its owner can neither resume, deploy nor wake it. Visitors see a neutral "This app is suspended" page. The owner is emailed the reason, the policy section, the duration and the appeal address (ABUSE_CONTACT_EMAIL, default [email protected]). Every decision is written to abuse_actions and the activity log. - Abuse reports: POST /api/v1/abuse/reports (rate-limited, honeypot) for the new /abuse page. Reports are matched to a project by host and land in the same queue. - npm run abuse:scan -- --dry-run (read-only): scans every live project with the same rules and prints what would be flagged. It clones each repository at the deployed commit into a temporary directory, scans the latest build log, and writes nothing. --enqueue instead puts flagged covered projects into the review queue (no suspension), so projects that were live before this release can be reviewed.

Fixed

  • A failed build no longer takes a Docker Compose project offline. The repository-compose deploy (compose_path) ran docker compose down before up -d --build, so a broken build: step left no container and nginx answered 502 until the next good deploy — the one path the blue-green "old container stays live" guarantee did not cover. Images are now pulled and built (compose build) while the previous stack keeps serving; only a successful build stops it, and up no longer rebuilds. A build failure is reported as Docker build failed (classified as a build error, not a crash).
  • A failed deploy no longer leaves a sleeping app down for good. A deploy marks an auto-sleep project awake before it builds. When the build failed, no new container existed and the previous one was still stopped by the sweeper, yet the project read awake, so the wake endpoint answered a permanent 502 and nothing ever started the container again. A failed deploy of an app that was asleep now puts it back to sleeping and wakes it, so the previous container serves again. If it cannot start, it stays sleeping and the next visit tries again.
  • A first deploy that failed to build shows "Deployment failed", not a crash page. When nginx falls back to the wake endpoint and the project's latest deployment failed with none ever running, visitors get a 503 "Deployment failed" page instead of the generic 502 "not responding".

v0.2.0-beta.105

Dashboard

Added

  • Nano servers in Servers → New. The size list can now show NANO (1 vCPU / 1 GB), which backend beta.99 offers on every paid plan when Hetzner has no cheaper 2 GB server in stock. Sizes under 1.5 GB carry a note that they come with 2 GB of swap and that large builds can be slow. The Servers doc describes it. Pricing tables already read live sizes, so Hobby's "server, included" line shows 1 vCPU / 1 GB when nano is the cheapest option.

v0.2.0-beta.100

Platform & API

Fixed

  • Deploys can check out the repository again. beta.98 accidentally committed node_modules as a symlink to a developer's local path. Checking it out tries to replace the real node_modules directory, so the Jenkins checkout failed with cannot unlink 'node_modules/…': Permission denied. The symlink is removed, and .gitignore now ignores node_modules whether it is a directory or a link.

v0.2.0-beta.99

Platform & API

Added

  • Nano managed servers (1 vCPU / 1 GB, Hetzner cpx02). A new nano size for when Hetzner has no cheap 2 GB+ server in stock (cx23 sold out). It is open on every paid plan, from Hobby up, and sits inside Hobby's included monthly credit. When an xs server is in stock for the same price or less, nano is not listed, because xs is then the better buy. Needs migration 0067 (server_size enum gains nano; additive).
  • New managed servers with under 1.5 GB of memory get a 2 GB swap file at setup, so image builds on the server don't run out of memory. Larger servers are unchanged.

v0.2.0-beta.98

Platform & API

Fixed

  • Server sizes, regions and public prices work again. Hetzner removed its /datacenters API (announced 2026-06-02, now answering 410 API functionality was removed). Stock was read from it, so GET /servers/providers/hetzner/sizes, /regions, the server quote and create flow and GET /public/managed-server-prices all failed. Stock now comes from each server type's locations[].available in /server_types.
  • Server details no longer crash on Hetzner's new server object. Servers come back without datacenter; the region and coordinates now come from the top-level location. Sync, status and the server list read servers through this path.

v0.2.0-beta.104

Dashboard

Fixed

  • /pushify-yaml shows the real limits. The page said 20 cron jobs, 5–3600 s timeouts and 10 volumes; the backend (beta.96) now uses one set for the file and the dashboard: 10 cron jobs per project (timeout 10–600 s), 5 volumes, 5 workers, counting what was added in the dashboard. The docs (Projects) say the same limits apply to pushify.yaml.

v0.2.0-beta.103

Dashboard

Added

  • Docs: verifying webhooks (Monitoring & alerts): what X-Pushify-Signature is, and a Node.js (Express) example that checks it against the raw body in constant time. Matches backend 0.2.0-beta.95, which now signs the exact body it sends.

v0.2.0-beta.102

Dashboard

Changed

  • /docs/teams shows who can do what for every role, matching the role checks in backend 0.2.0-beta.93. It also says that API keys act with their creator's role and stop when that person leaves.
  • Billing page: the wallet, the invoices and the subscription status are shown to owners and admins only. The API now refuses these to everyone else.

v0.2.0-beta.101

Dashboard

Added

  • Product documentation at /docs. It has 13 pages: Getting started, Servers, Projects & builds, Environment variables, Domains & HTTPS, Databases & backups, Preview deployments, Scaling & sleep, Monitoring & alerts, Teams & SSO, Billing & credits, CLI and Self-hosting. - Pages are written in content/docs/*.md and rendered on the server. - Each page has a sidebar, an "on this page" list and previous/next links. - Every page was checked against the code on 2026-09-30.
  • Markdown tables in the blog and docs.

Changed

  • The API reference moved to /docs/api. - Old /docs#<section> links forward there. - /docs/quickstart, /docs/deploy and /docs/preview redirect to their new pages. - The temporary redirects that sent /docs/servers, /docs/cli and others to anchors or to npm are gone.
  • API reference fixes: isSecret defaults to false, not true; the scope list adds projects:delete and metrics:read.

v0.2.0-beta.100

Dashboard

Added

  • Deletion confirmed by email. Accounts with neither a password nor two-factor authentication now confirm a deletion by email. - The delete dialog explains this, and its button reads "Email me the link". - After sending, it shows "Check your email". - The link opens /confirm-deletion, which starts the deletion and shows its date. For an account deletion, it also signs this browser out. Needs backend 0.2.0-beta.89.

v0.2.0-beta.97

Platform & API

Fixed

  • Sleep stops every replica, and wake starts them all. Sleep only matched the primary container (pushify-<slug>-blue), so replicas (-blue-2, -blue-3, …) kept running and serving traffic while the project showed as sleeping. It now matches every replica of either slot, leaving workers, previews and the database sidecar alone. Wake used to count as done as soon as one container was running. It now starts all of them and succeeds only when none is left stopped, retrying within the 30 s window.

v0.2.0-beta.96

Platform & API

Fixed

  • pushify.yaml limits match the dashboard. The file accepted 20 cron jobs, 5–3600 s timeouts and 10 volumes, while the dashboard and API allowed 10, 10–600 s and 5. Both now read one definition (src/lib/project-limits.ts): 10 cron jobs per project (timeout 10–600 s, default 120), 5 volumes, 5 workers. A pushify.yaml over these limits is reported as invalid in the deploy log and ignored, like any other invalid file.
  • The deploy-time sync of pushify.yaml now counts what the project already has. A new cron job, volume or worker that would go over the limit is skipped with a line in the deploy log; the deploy continues.

Added

  • npm run limits:over (read-only): lists projects that already hold more cron jobs, volumes or workers than the limits, and cron jobs whose timeout is outside 10–600 s. Nothing is changed.

v0.2.0-beta.95

Platform & API

Fixed

  • Webhook signatures can be verified. X-Pushify-Signature was computed over the event payload, but the request body wraps it ({ event, timestamp, data }), so a receiver checking the signature against the body it received always failed. The signature is now sha256= + HMAC-SHA256 of the exact request body, keyed with the channel's secret. Receivers that worked around the old behaviour by re-serialising `data` must switch to the raw body. A Node.js verification example is in the docs (Monitoring & alerts).

v0.2.0-beta.94

Platform & API

Fixed

  • Bulk env var upserts (`pushify env push`) no longer overwrite a secret with its masked copy. Pushing back a .env from pushify env pull sent ab****yz for every secret and replaced the real value; an unchanged mask now leaves the variable as it is (action: "unchanged"). isSecret is only changed when the request sets it, and the response reports the stored value instead of false.

Added

  • npm run env:masked-values (read-only) lists variables whose stored value looks like a mask — the ones this may have hit — by project and key, never the values.

v0.2.0-beta.93

Platform & API

Security

  • Role checks across the API. A scan of every endpoint found about 40% of the sensitive or destructive ones open to viewers, and around 30 routes that did not check membership at all. They now go through one helper (lib/org-access.ts: viewer < member < admin < owner). Owners and admins only: - database credentials and backup downloads; - buying and transferring domains and taking their transfer code; - server power; - invoices and wallet history; - the activity log. Member and up: - deploying, rolling back, cancelling and promoting; - scheduled tasks (which run commands in the container); - creating, pausing and resuming projects; - notifications, health checks, volumes and the site editor. Viewers see environment variable names only.
  • API keys can no longer create API keys or approve a CLI login. A member's keys are revoked when they leave, and a key stops working as soon as its owner is no longer a member. Scopes are now enforced on the project sub-routes that lacked them (logs:read, metrics:read included).
  • Restricted members are kept to their projects on notifications, health checks, previews, webhooks and the metrics overview.

v0.2.0-beta.92

Platform & API

Fixed

  • CLI browser login works when the API runs more than one process. Sessions move from process memory to Redis (in memory only when Redis is not configured). - They expire with the code, after 10 minutes. - Who approved is stored encrypted. - Collecting the key removes the session atomically, so concurrent polls on different processes cannot both get a key.

v0.2.0-beta.91

Platform & API

Added

  • Managed servers left off for non-payment are deleted after 30 days. This covers servers powered off because the subscription ended or the wallet ran dry. - Warning emails go out on day 14 and day 27. On day 30 the server is deleted with its snapshots and SSH key, and a final email is sent. - Topping up, renewing the plan or starting the server stops the clock. - Servers the customer stopped themselves are not affected.
  • Migration 0065_stopped_servers adds servers.stopped_at and stop_warning_step.

v0.2.0-beta.90

Platform & API

Added

  • Retention periods for records that were kept forever, applied by a daily sweep: - organization activity log: 1 year; - sign-in history: 90 days; - build and deploy output: 90 days. The deployment row stays, and every project keeps full logs for its latest 10 deployments; - expired sessions and password-reset/verification tokens: 1 day after they expire.
  • Migration 0064_retention_indexes adds created_at indexes on activity_logs and deployments.

v0.2.0-beta.89

Platform & API

Changed

  • Accounts with neither a password nor 2FA confirm a deletion by email. - Deleting an organization or the account sends a link instead of starting the deletion. The link works once and is valid for one hour. - The deletion starts only when the link is clicked. The request then answers 202 { confirmationSent: true }. - The link goes through POST /auth/deletion/confirm, which checks everything again (still the owner, nothing already scheduled).

v0.2.0-beta.88

Platform & API

Fixed

  • Deleting a database removes its data from the server. This covers the data directory with its local backups, and the earlier copies that provisioning set aside in .old/<name>-*. Until now only the container was removed.
  • CLI browser login no longer holds an API key server-side. - Approving now only records who approved it. The key is created when the CLI collects it, and handed out once. - An approval nobody collects leaves no key behind. Before, it left a full-scope key the user never saw. - The plain login code is no longer stored, only its hash. - No key is issued once the organization is scheduled for deletion.