Appsmith is a self-hosted low-code builder for internal tools — admin panels, approval dashboards, CRUD frontends over whatever databases and APIs you already have. Deploying it is different from deploying a typical web app, and that difference trips people up: there is no build step for "your app." The apps you build in Appsmith live inside Appsmith's own database. What you deploy is the Appsmith server itself, and the whole game is making sure its state survives redeploys.
Here's the setup I'd run in production: the Appsmith container on PandaStack, with its internal state externalized to managed MongoDB and Redis, and a managed PostgreSQL attached as the datasource for the tools you build.
How Appsmith is put together
The official image, appsmith/appsmith-ce, is an all-in-one container: the Java backend, the client, and — by default — an *embedded* MongoDB and Redis, all managed by a supervisor process. It listens on port 80 and keeps everything under /appsmith-stacks.
That embedded default is fine on a laptop and dangerous on a platform, because container filesystems are ephemeral. Redeploy the container without a persistent volume and the embedded MongoDB — holding every app anyone built — is gone.
The fix is the one Appsmith's own [external-database docs](https://docs.appsmith.com/getting-started/setup/instance-configuration/custom-mongodb-redis) describe: point Appsmith at an external MongoDB and Redis via environment variables. Then the container itself is disposable, which is exactly what you want on a modern platform.
Two constraints worth knowing up front:
- Appsmith requires MongoDB to run as a replica set (it uses change streams and transactions). A standalone
mongodwill not work. - Appsmith is a Java application with a real appetite. Its docs recommend roughly 2 vCPU and 4 GB of RAM for a self-hosted instance — the 0.25 CPU / 512 MB free tier is not going to cut it. Plan on one of the larger compute tiers.
Step 1: Provision the state stores
In the PandaStack dashboard, create two managed databases:
- 1MongoDB — this becomes Appsmith's internal store (apps, pages, users, datasource definitions).
- 2Redis — sessions and caching.
Both are managed engines on PandaStack (orchestrated by KubeBlocks on Kubernetes), with scheduled daily backups retained 7/15/30 days depending on plan. The MongoDB backup is the one that matters here: it's effectively a backup of every internal tool your team builds.
Grab the connection strings for both from the dashboard.
Step 2: The repo
PandaStack deploys container apps from a Git repo, and since Appsmith ships as a prebuilt image, the repo is nearly trivial — a one-line Dockerfile:
# Dockerfile
FROM appsmith/appsmith-ce:latestIn practice, replace latest with the current release tag from [Appsmith's releases page](https://github.com/appsmithorg/appsmith/releases) and pin it. Appsmith releases frequently, and you want upgrades to happen when *you* push, not whenever a rebuild happens to pull a newer image. With a pinned tag, upgrading later is a one-line diff and a git push.
git init appsmith-deploy && cd appsmith-deploy
echo 'FROM appsmith/appsmith-ce:latest' > Dockerfile # then pin a real tag
git add . && git commit -m "Deploy Appsmith"
git push -u origin mainConnect this repo as a container app in the PandaStack dashboard. The build runs in a rootless BuildKit job on Kubernetes and you can watch it in the live build logs. Set the app's port to 80 — that's where Appsmith's internal Nginx listens.
Step 3: Environment variables — the part that actually matters
This is where a fresh container becomes *your* Appsmith instead of a blank one. Set these in the app's environment variables:
# Internal state — external MongoDB (must be a replica set) and Redis
APPSMITH_DB_URL=mongodb://<user>:<pass>@<mongo-host>:27017/appsmith?replicaSet=<rs-name>&authSource=admin
APPSMITH_REDIS_URL=redis://<user>:<pass>@<redis-host>:6379
# Encryption keys — generate once, never change, never lose
APPSMITH_ENCRYPTION_PASSWORD=<openssl rand -base64 32>
APPSMITH_ENCRYPTION_SALT=<openssl rand -base64 32>
# Optional but sensible
APPSMITH_DISABLE_TELEMETRY=trueThree things senior-engineer-to-senior-engineer:
The encryption pair is load-bearing. Appsmith encrypts datasource credentials (your Postgres passwords, API keys) at rest using APPSMITH_ENCRYPTION_PASSWORD and APPSMITH_ENCRYPTION_SALT. If a new container boots with different values, it can read the MongoDB just fine but cannot decrypt any stored credentials — every datasource breaks at once. Generate them once with openssl rand -base64 32, set them as env vars, and treat them like the root secret they are.
APPSMITH_DB_URL replaced APPSMITH_MONGODB_URI. Older tutorials use the deprecated name. Use APPSMITH_DB_URL; recent Appsmith releases have also been adding PostgreSQL support as the internal store — check [their docs](https://docs.appsmith.com/) for the current state before relying on it. MongoDB is the well-trodden path.
Run one replica. Horizontal scaling of Appsmith has its own requirements — don't casually bump the replica count and assume sessions and locks will sort themselves out. One adequately-sized instance handles a lot of internal-tool traffic.
Because all state now lives in managed MongoDB and Redis, and the secrets arrive via environment variables, the container needs no persistent volume. Redeploys, rollbacks, version bumps — all safe.
Step 4: First boot and the admin account
Deploy, tail the live logs, and give it a minute — a Java backend plus supervisor is not a 200 ms cold start. One consequence: if you ever do run Appsmith on a scale-to-zero tier, the wake-up on first request will be slow. For a tool your whole team uses daily, keep it on an always-on paid tier.
Open the app URL. The first account you create becomes the instance administrator, so do this immediately after deploying — don't leave a fresh Appsmith signup page reachable on the public internet overnight. From the admin settings you can then restrict signups entirely or wire up SSO.
Add a custom domain like tools.yourcompany.com in the dashboard; SSL is automatic.
Step 5: Connect Postgres for the tools you'll build
Now the part Appsmith exists for. Create a managed PostgreSQL (14.x or 16.x) in PandaStack — this is the database your internal tools will read and write, separate from Appsmith's internal MongoDB.
One honest note: PandaStack injects DATABASE_URL automatically into apps a database is attached to, but Appsmith doesn't read DATABASE_URL — datasources are configured through its UI. So take the connection details from the dashboard and create the datasource inside Appsmith: Datasources → New → PostgreSQL, then fill in host, port 5432, database name, user, and password, and enable SSL. Hit Test before saving.
Those credentials are encrypted with the keys from Step 3 and stored in MongoDB — which is why that pairing mattered.
From here it's normal Appsmith: write a query, drag a table widget, bind {{ users_query.data }}, and you've got an admin panel your support team can actually use.
Upgrades and day two
- Upgrade: change the image tag in the Dockerfile,
git push. State is external, so the new container comes up against the same MongoDB. Check Appsmith's release notes for migration notes between major versions. - Rollback: PandaStack keeps deployment history, so a bad version bump is reversible from the dashboard. Roll back the *container* freely — Appsmith handles its own internal schema migrations forward.
- Backups: your disaster-recovery story is the managed MongoDB backups plus the encryption pair stored somewhere safe. Test that you actually have both.
The pattern here generalizes: any stateful self-hosted tool (Metabase, n8n, and friends) becomes pleasant to run the moment you externalize its state to managed databases and let the container be cattle. If you want to try this setup, the free tier at [pandastack.io](https://pandastack.io) is enough to kick the tires.