How to Deploy Instant – Open-Source Firebase Alternative

Instant is an open-source, real-time backend platform for web applications. It provides a client-side database with built-in support for authentication, relational queries, transactions, file storage, and real-time data synchronization. Instant serves as a self-hosted alternative to Firebase and removes the need to manage custom backend APIs or WebSocket infrastructure.
This article explains how to deploy Instant on a Linux server using Docker Compose. It covers directory setup, environment configuration, Traefik reverse proxy integration for automatic HTTPS, and backend API verification using a sample database workflow.
Prerequisites
Before you begin, you need to:
- Have access to a Linux-based server (with at least 2 CPU cores and 4 GB of RAM) as a non-root user with sudo privileges.
- Install Docker and Docker Compose.
- Create a DNS A record pointing to your server's IP address (for example,
instant.example.com).
Set Up the Directory Structure, Configuration, and Environment Variables
Instant runs as a containerized backend service built from the official source repository. The project directory stores the source code, Docker Compose configuration, and environment variables.
Create a project directory for Instant and navigate into it.
console$ mkdir ~/instant $ cd ~/instant
Clone the official Instant repository. InstantDB does not publish pre-built Docker images or versioned releases, so the server is built from source using the official repository.
console$ git clone https://github.com/instantdb/instant.git app
The repository is cloned into a folder named
appto keep the project path clean.Navigate to the server directory.
console$ cd app/server
Rename the default
docker-compose.ymlfile to keep it as a backup.console$ mv docker-compose.yml docker-compose.yml.bak
Create the environment configuration file.
console$ nano .env
Add the following configuration:
iniTZ=UTC DOMAIN=instant.example.com EMAIL=admin@example.com POSTGRES_USER=instant POSTGRES_PASSWORD=YOUR_DATABASE_PASSWORD POSTGRES_DB=instant
Replace the placeholders:
- instant.example.com: Your registered domain name.
- admin@example.com: Your email address for Let's Encrypt notifications.
- YOUR_DATABASE_PASSWORD: A strong password for the PostgreSQL database.
Save and close the file.
Deploy with Docker Compose
The deployment stack consists of three services: Traefik handles reverse proxy and automatic TLS certificate management via Let's Encrypt, PostgreSQL stores application data with logical replication enabled, and the Instant server runs the backend application.
Create the Docker Compose configuration file.
console$ nano docker-compose.yml
Add the following configuration:
yamlservices: traefik: image: traefik:v3.6 container_name: traefik command: - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.web.http.redirections.entrypoint.scheme=https" - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true" - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web" - "--certificatesresolvers.letsencrypt.acme.email=${EMAIL}" - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" ports: - "80:80" - "443:443" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" - "./letsencrypt:/letsencrypt" restart: unless-stopped postgres: image: ghcr.io/instantdb/postgresql:postgresql-16-pg-hint-plan container_name: postgres environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} volumes: - ./postgres-data:/var/lib/postgresql/data command: - postgres - -c - wal_level=logical - -c - max_replication_slots=4 - -c - max_wal_senders=4 - -c - shared_preload_libraries=pg_hint_plan - -c - random_page_cost=1.1 healthcheck: test: ["CMD", "pg_isready", "-U", "instant"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped server: build: context: . dockerfile: Dockerfile-dev container_name: instant working_dir: /app command: ["make", "dev-oss"] depends_on: postgres: condition: service_healthy environment: TZ: ${TZ} DATABASE_URL: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}" NREPL_BIND_ADDRESS: "0.0.0.0" volumes: - ./:/app expose: - "8888" labels: - "traefik.enable=true" - "traefik.http.routers.instant.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.instant.entrypoints=websecure" - "traefik.http.routers.instant.tls.certresolver=letsencrypt" - "traefik.http.services.instant.loadbalancer.server.port=8888" restart: unless-stopped
Save and close the file.
In the above manifest:
- services: Launches three containers managed by Docker Compose:
- traefik: Functions as the reverse proxy and TLS termination point.
- postgres: Runs the Instant-specific PostgreSQL image with required extensions.
- server: Builds and runs the Instant backend application.
- image: Defines which container image each service uses. The PostgreSQL image is maintained by InstantDB and includes the
pg_hint_planextension required for query optimization. - container_name: Assigns predictable names to containers, simplifying log inspection and management commands.
- command (Traefik): Establishes Traefik's operational parameters: Docker provider integration, HTTP and HTTPS entry points, automatic redirection from HTTP to HTTPS, and Let's Encrypt certificate acquisition.
- command (PostgreSQL): Enables logical replication and performance tuning parameters required by Instant for real-time data synchronization.
- build (server): Builds the Instant backend image from the Dockerfile in the repository.
- depends_on: Ensures the server starts only after PostgreSQL reports a healthy state.
- expose (server): Makes port
8888available internally for Traefik routing without exposing it directly to the host network. - volumes:
- The
./postgres-databind mount stores database files on the host persistently. - The
./letsencryptbind mount retains TLS certificates between container restarts. - The Docker socket mount (
/var/run/docker.sock) enables Traefik to detect and configure routes for running containers automatically.
- The
- labels (server): Instructs Traefik to route HTTPS traffic for the configured domain to the Instant backend on port
8888. - healthcheck: Verifies PostgreSQL readiness before the backend starts.
- restart: unless-stopped: Configures containers to restart automatically after crashes or server reboots, unless explicitly stopped.
- services: Launches three containers managed by Docker Compose:
Start the containers in detached mode.
console$ docker compose up -d --build
The
--buildflag builds the Instant server image from source on first run.Verify that all containers are running.
console$ docker compose ps
The output displays three running containers with Traefik listening on ports 80 and 443, PostgreSQL reporting a healthy status, and the Instant server exposing port 8888.
View the logs for the services.
console$ docker compose logs
The output shows PostgreSQL initialization messages, the Instant server startup sequence, and Traefik registering routes for the configured domain.
For more information on managing a Docker Compose stack, see the How to Use Docker Compose article.
Access and Configure Instant
The Instant backend exposes a REST API for application management and real-time data operations. Traefik routes HTTPS traffic to the backend and automatically provisions TLS certificates from Let's Encrypt.
Verify that the Instant backend is accessible. Replace
instant.example.comwith your configured domain name.console$ curl https://instant.example.com
Output:
<code>Welcome to Instant's Backend!</code>The backend completes initialization after the containers start. If the curl command returns a connection error, wait a few moments for initialization to complete and run the command again. Let's Encrypt certificate provisioning may also take up to a minute on first access.NoteOpen your web browser and navigate to
https://instant.example.com, replacinginstant.example.comwith your configured domain name.The page displays the welcome message, confirming HTTPS routing through Traefik is working correctly.
Verify Database Connectivity
Instant uses PostgreSQL as its persistent store for application data. The following steps confirm database connectivity and query functionality by creating a test table, inserting sample data, and querying it from the PostgreSQL container.
Connect to the PostgreSQL container.
console$ docker compose exec postgres psql -U instant -d instant
The prompt changes to
instant=#on successful connection.Create a sample table.
psqlinstant=# CREATE TABLE messages (id SERIAL PRIMARY KEY, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW());
Insert test records into the table.
psqlinstant=# INSERT INTO messages (content) VALUES ('Hello from Instant'), ('Real-time sync test');
Output:
INSERT 0 2Query the stored data.
psqlinstant=# SELECT id, content, created_at FROM messages ORDER BY created_at DESC;
The output displays both inserted rows with their auto-generated IDs and timestamps reflecting the current time.
Exit the database shell.
psqlinstant=# \q
Review the backend logs to confirm that the server is running without errors.
console$ docker compose logs --tail=20 server
The logs display initialization messages and confirm the server is accepting connections.
Conclusion
You have successfully deployed Instant on your Linux server using Docker Compose with PostgreSQL as the backend database and Traefik for automatic HTTPS. The setup provides a self-hosted real-time backend platform with persistent storage, authentication support, and relational query capabilities. Instant eliminates the need for custom backend APIs and WebSocket infrastructure while providing Firebase-like functionality. For advanced configuration, client SDK integration, and API reference, refer to the official Instant documentation.