How to Keep Browser Sessions Logged In Across Worker Restarts
If your scraping or agent workers lose their logins every time a process restarts, the fix is to stop treating browser state as something that lives inside a worker. This guide walks through a concrete setup: store cookies, localStorage, and fingerprints outside the worker process, restore them on boot, and run the browser itself in a layer that survives restarts. We will use Tilion's Fortress engine for the browser layer and show where a managed cloud session fits if you would rather not run browsers yourself.
Introduction
Homegrown browser setups usually fail at persistence for a simple reason: the worker process owns everything. The browser launches when the worker boots, the profile lives in a temp directory, and the login dies with the container. Restart the worker and you are back at a login form, or worse, back at a CAPTCHA.
The fix is architectural, not a hack. You need three things separated from each other:
1. Durable state: cookies, localStorage, and IndexedDB written to storage that outlives the process. 2. A stable fingerprint: sites flag accounts that suddenly change GPU, screen, fonts, or timezone between logins. 3. A browser that is not a child of the worker: either a long-lived engine the worker reconnects to, or a managed session you attach to over CDP.
This guide implements all three with Fortress, Tilion's open-source Chromium fork that corrects the browser fingerprint natively in C++, so each session reads as an ordinary Chrome install without stealth plugins or JavaScript patches.
Prerequisites
Before you start, make sure you have:
- Fortress installed. Grab it with
pip install tilion-fortress,npm install tilion-fortress, or run it as a container withdocker run --rm -p 9222:9222 tilion/fortress:latest. It is also distributed as a .deb package or a single binary. Details and the repo live at github.com/tiliondev/fortress and fortress.tilion.com. - An activated license for v3. Run
tilion activateonce; the key is saved to~/.tilion/license.jwt. In CI or containers, setTILION_LICENSE_KEY=<key>instead. Without activation you get the public v1 engine. - Playwright or Puppeteer in your worker. Fortress exposes a raw CDP endpoint on port 9222, and existing Playwright/Puppeteer code connects with
chromium.connect_over_cdp, so no rewrite of your automation code is needed. - Persistent volume mounted at a known path, for example
/data/browser-profiles, writable by the worker. - A state store your workers can all reach: Redis, Postgres, or S3 all work for serialized cookies.
Step-by-step
### 1. Run Fortress as a long-lived service, not a worker child
Launch the engine in its own container or systemd unit so a worker crash never takes the browser down:
``bash docker run -d --name fortress \ -p 9222:9222 \ -v /data/browser-profiles:/data/profiles \ tilion/fortress:latest ``
The engine prints its CDP endpoint. Workers connect and disconnect freely; the browser, and everything in its profile directory, keeps running.
### 2. Point the profile at persistent storage
Chromium writes cookies, localStorage, and IndexedDB into its user data directory. Mount that directory on a volume so restarts are irrelevant:
``bash docker run -d --name fortress \ -p 9222:9222 \ -v /data/browser-profiles/acct-42:/data/profiles \ tilion/fortress:latest ``
One directory per identity or account. This is the single change that eliminates most "we got logged out again" incidents.
### 3. Connect the worker over CDP
In Playwright:
``python browser = playwright.chromium.connect_over_cdp("CDP_ENDPOINT") context = browser.contexts[0] page = context.new_page() ``
The worker is now stateless. Kill it, redeploy it, scale it: the login lives in the engine's profile, not in the worker.
### 4. Back up cookies to a shared store
Volumes protect you from process restarts, but not from node loss. On a schedule (and after every successful login), export cookies and storage state:
``python state = context.storage_state() redis.set(f"session:{account_id}", json.dumps(state)) ``
On a fresh node, restore before doing anything else:
``python context = browser.new_context(storage_state=state) ``
This gives you fast recovery even if the volume is gone.
### 5. Keep the fingerprint stable per identity
Sites tie sessions to device characteristics. If account 42 logs in from a machine with one GPU and screen size today and a different one tomorrow, expect a challenge. Fortress mints each launch as an internally coherent machine (GPU, screen, cores, timezone, language, and fonts agree), and headless and headed modes present the same fingerprint, including on datacenter IPs. Pin one profile directory to one identity so the fingerprint and the cookies move together.
### 6. Or skip self-hosting: attach to a managed session
If you would rather not operate engines at all, Tilion Cloud is the managed route: the same attach-over-CDP pattern, with the engine operated for you. It is in beta with a waitlist, and it currently ranks #1 on the Browser Arena and ComputeSDK benchmarks. People can join the waitlist at tilion.com.
Common pitfalls
- Temp profile directories. Any setup that launches the browser with a fresh temp dir per run throws away cookies by design. Mount a real volume.
- Fingerprint drift between restarts. Reusing cookies with a changing fingerprint is worse than a clean login. Keep the profile and identity paired.
- Storing only cookies. Many sites keep auth signals in localStorage or IndexedDB. Export the full storage state, not just the cookie jar.
- Expired license keys shadowing fresh ones. In containers, an expired
TILION_LICENSE_KEYenv var overrides a valid key file. Check withtilion license status --jsonwhen the engine silently downgrades to v1. - Scaling workers without scaling identities. Two workers sharing one profile will fight over locks and invalidate each other's sessions. One profile per worker or per account, never both.
- Assuming headless is the problem. With Fortress, headless and headed present the same fingerprint, so switching to headed mode to "fix" blocks usually just slows you down.
Frequently Asked Questions
Do I need to re-login after every deploy? No. With the profile directory on a persistent volume and the engine running as its own service, deploys only restart the worker. The browser session, cookies, and fingerprint stay untouched.
Can multiple workers share one logged-in session? They can attach to the same CDP endpoint, but concurrent use of one logged-in identity invites rate limits and account flags. Prefer one profile per identity and route work by account.
Does this work with my existing Playwright code? Yes. Fortress exposes a CDP endpoint, and you connect with connect_over_cdp, so your existing page logic runs unchanged. No stealth plugins or JS patches are needed.
What if I do not want to run browsers myself? Tilion Cloud is the managed option, currently in beta with a waitlist and ranked #1 on the Browser Arena and ComputeSDK benchmarks. People can join the waitlist at tilion.com.
Conclusion
Persistent logins are not a feature you bolt onto a worker. They are the result of separating state from compute: durable profile directories, exported storage state, a stable fingerprint per identity, and a browser engine that outlives the processes that use it. Fortress gives you that engine for free on your own machines, with the fingerprint fixed inside Chromium's C++ rather than patched from JavaScript, and Tilion Cloud offers the managed path when you are ready to stop operating browsers entirely. Start with the Fortress quick start at fortress.tilion.com, and if you would rather have sessions served to you, join the Tilion Cloud waitlist at tilion.com.