Self-hosted, fully-offline Photopea (rebranded Photoshop) + AI plugins — docker/nginx recipe. Proprietary payload not included; build with ops/update.sh.
  • JavaScript 63.6%
  • Shell 25.7%
  • HTML 10.1%
  • Dockerfile 0.6%
Find a file
admin_ekaii 7652ef0ec4 Self-hosted offline Photoshop (Photopea-based) — mirror recipe, offline patches, docker/nginx, AI plugins
Tooling to run Photopea fully offline and self-hosted, rebranded to 'Photoshop'
with a rebuild disclaimer:
- ops/: mirror + patch + update scripts, nginx + docker-compose, font extractor
- offline hardening: strip telemetry/ads, self-host lock unlock, ad guard, CDN guard
- rebrand Photopea->Photoshop + disclaimer, plugin auto-registrar
- site/plugins/removebg: self-hosted Remove BG plugin (wired to rembg via /ai proxy)
- verify/: network-blocked Playwright harnesses
- docs/: reverse-engineering + architecture notes

The proprietary Photopea payload (site/ assets) is NOT included; build it with ops/update.sh.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 11:48:35 +02:00
docs Self-hosted offline Photoshop (Photopea-based) — mirror recipe, offline patches, docker/nginx, AI plugins 2026-07-15 11:48:35 +02:00
ops Self-hosted offline Photoshop (Photopea-based) — mirror recipe, offline patches, docker/nginx, AI plugins 2026-07-15 11:48:35 +02:00
site/plugins/removebg Self-hosted offline Photoshop (Photopea-based) — mirror recipe, offline patches, docker/nginx, AI plugins 2026-07-15 11:48:35 +02:00
verify Self-hosted offline Photoshop (Photopea-based) — mirror recipe, offline patches, docker/nginx, AI plugins 2026-07-15 11:48:35 +02:00
.gitignore Self-hosted offline Photoshop (Photopea-based) — mirror recipe, offline patches, docker/nginx, AI plugins 2026-07-15 11:48:35 +02:00
docker-compose.baked.yml Self-hosted offline Photoshop (Photopea-based) — mirror recipe, offline patches, docker/nginx, AI plugins 2026-07-15 11:48:35 +02:00
docker-compose.yml Self-hosted offline Photoshop (Photopea-based) — mirror recipe, offline patches, docker/nginx, AI plugins 2026-07-15 11:48:35 +02:00
Dockerfile Self-hosted offline Photoshop (Photopea-based) — mirror recipe, offline patches, docker/nginx, AI plugins 2026-07-15 11:48:35 +02:00
README.md Self-hosted offline Photoshop (Photopea-based) — mirror recipe, offline patches, docker/nginx, AI plugins 2026-07-15 11:48:35 +02:00

Photopea — fully offline, self-hosted

A complete local mirror of Photopea served by nginx in Docker. Runs with zero network access — verified by an adversarial test that blocks every non-localhost request while driving a full editing workflow. Built for working offline (on a plane, off-grid, wherever) with no dependency on the internet.

http://127.0.0.1:8887

Quick start

cd ~/photopea-offline
docker compose up -d          # nginx serving ./site on 127.0.0.1:8887
open http://127.0.0.1:8887    # or http://127.0.0.1:8887/#  to skip the splash

Stop / start:

docker compose down
docker compose up -d

What's inside

Path What
site/ The whole Photopea snapshot — 6090 files, ~2.8 GB (code, resources, the full 5912-file font library, JXL/HEIF/AVIF codecs, UI fonts).
docker-compose.yml nginx:alpine, bind-mounts site/, binds to 127.0.0.1:8887 only.
ops/nginx.conf MIME/gzip/cache config + a local /papi/ stub so telemetry never leaves the machine.
ops/mirror.sh Downloads every asset in ops/manifest-all.txt (resumable).
ops/patch.sh Offline-hardening source patches (see below).
ops/update.sh Refresh the snapshot from the live site when online.
ops/extract-fonts.js Decodes the font DB out of DBS*.js to regenerate the font list.
verify/matrix.js, verify/matrix2.js The adversarial offline tests.
Dockerfile + docker-compose.baked.yml Optional: bake the snapshot into a portable image.

Verified working offline (network fully blocked)

Two Playwright harnesses drive real editing while aborting and logging every request that isn't 127.0.0.1. Latest run: all checks green, 0 external requests, 0 missing assets, 0 console errors.

  • App loads; new document; text with the on-demand font library (fonts stream from the local mirror, both the fs/ and gf/ pools); HarfBuzz/FriBidi text shaping (hb.wasm).
  • Filters (Gaussian/Motion blur, Unsharp, Add Noise); adjustments (Levels, Desaturate, rotate, resize).
  • Open & save PSD (round-trip verified — save then reopen). Open PNG.
  • Export PSD, PNG, JPG, WEBP, GIF, BMP, TIFF, SVG, PDF (all valid signatures).
  • Resource packs (basic.zip, fa.zip custom shapes) load from the mirror.

Re-run any time (container must be up):

cd verify && node matrix.js      # core workflow (14 checks)
node matrix2.js                  # codecs + filters + adjustments (13 checks)

The harness temporarily writes site/_verify_wrapper.html and removes it on exit.

What does NOT work offline (by design — these need Photopea's servers)

None of these are fixable by a mirror; they call online-only backends:

  • AI features — Remove Background, Generative Fill/Replace, upscaling.
  • Account / premium — login, buying premium, license keys (stubbed to no-ops).
  • Cloud storage — Google Drive, Dropbox, OneDrive, PeaDrive.
  • Open image from URL and the Templates gallery (fetch from remote servers).
  • Codec files for HEIC/AVIF/JXL are mirrored and served (they load same-origin, so decoding a local HEIC/AVIF should work offline), but that path is exercised through the Open/Export dialog UI, not the scripting API, so it wasn't asserted end-to-end by the automated harness.

Everything else — open/edit/save all common raster & vector formats, layers, masks, smart objects, adjustment layers, channels, paths, brushes, text, filters, Camera Raw (RAW decode is built in) — works fully offline.

Offline hardening applied (ops/patch.sh)

Surgical string patches to pp.js + index.html (validated with node --check):

  • Zero egress. papi/* telemetry & account calls are rewritten to resolve at the local nginx (which answers {}), so nothing leaves the machine even when the laptop is online.
  • Ads removed. All four ad-script injectors neutralized (Google Publisher Tag via doubleclick/googletagservices, plus nuload.net/photopea/ad.js), and the "Are you blocking ads?" nag dialog removed from the notification rotation.
  • CDN-escape guard. aw.Sk() is forced to resolve fonts/icons relative, so the app can never reach out to vecpea.com even if served from a photopea.com hostname.
  • Google Fonts (Open Sans UI font) downloaded to site/style/gfonts/ and the CSS @font-face URLs rewritten local.

Updating the snapshot (do this online)

Photopea version-stamps its core files (pp<N>.js, DBS<N>.js, …) every release. To pull a fresh copy:

ops/update.sh

It re-derives the current core filenames from index.html, regenerates the font list from the new DBS, downloads new/changed files (and prunes fonts dropped from the DB), re-localizes the UI font CSS, re-applies the patches, and restarts the container. Then re-run verify/matrix.js to confirm.

Portable image (carry it to another machine)

docker compose -f docker-compose.baked.yml build      # bakes site/ into the image
docker save photopea-offline:latest | zstd > photopea-offline.tar.zst
# on the other machine:
zstd -d photopea-offline.tar.zst -o - | docker load
docker run -d -p 127.0.0.1:8887:80 photopea-offline:latest

A note on licensing

Photopea is proprietary freeware (author: Ivan Kutskir). He explicitly states it "runs locally already… no data is sent" and is usable offline after caching, but objects to public re-hosting / competing / ad-supported mirrors (he has DMCA'd those). This snapshot is for private, single-user, ad-free, offline use on your own machine. Keep it that way: don't expose it publicly, don't add ads, don't present it as a service. If you deploy Photopea for an organization/many users, he sells a proper self-hosted license (photopea.com/api/accounts#self-hosted).