How to setup Firebase Emulator in Docker
And also persisting the emulator data
Firebase offers an emulator suite that make developing applications locally easier, but integrating it into a Docker environment can streamline your workflow even further. In this tutorial, we'll explore how to use the Firebase Emulator within Docker. In the end I'll show a ready-to-use Docker image I've prepared, which encapsulates all the steps we'll discuss in this article.
Why Dockerize Firebase Emulator?
Dockerizing the Firebase Emulator offers several benefits:
Consistency: Docker ensures that the Firebase Emulator runs the same way, regardless of where it's deployed, by encapsulating dependencies and configurations.
Simplicity: By using Docker, setting up the Firebase Emulator becomes a matter of running a container, without the need to manually install dependencies on your local machine or CI/CD environments.
Portability: Containers can be easily shared and run across different environments, making collaboration and deployment seamless.
Why another Image?
I know, there are some existing images out there, which we were also using, namely this one: https://hub.docker.com/r/spine3/firebase-emulator. However, we weren't able to make the image persist data, as the container never shuts down gracefully and never reaches the point where data gets exported. Without the underlying Dockerfile we weren't even able to dig deeper, so we thought our time is spent better in making a new setup and open sourcing this. So here we go:
Prerequisites
Before we dive into the setup, ensure you have the following installed:
Docker: Install Docker
Git (optional): For cloning the repository, though you can also download the code directly.
Step 1: Setting up your Firebase config
First of all we need a firebase.json
config file that the emulator picks up on startup. You can get one by installing the firebase CLI and then run firebase init
in a folder or you copy the one below.
Create a new folder, I'll name mine
firebase-docker
but the name is up to youChange into that directory and create a new file
firebase.json
(feel free to copy the one below)(optional) customize the ports if needed
{
"emulators": {
"firestore": { "port": "8080", "host": "0.0.0.0" },
"ui": { "enabled": true, "port": "4000", "host": "0.0.0.0" },
"auth": { "port": "9099", "host": "0.0.0.0" },
"functions": { "port": "5001", "host": "0.0.0.0" },
"database": { "port": "9000", "host": "0.0.0.0" },
"storage": { "port": "9199", "host": "0.0.0.0" }
},
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"storage": {
"rules": "storage.rules"
}
}
As you can see the firebase-config references to two .rules
-files. Those are needed for those emulators to know how to handle requests to these services. In our case we want every request to be allowed without verification.
- Create two new files by running
touch storage.rules firestore.rules
in our directory and add the following contents to them
// firestore.rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
// storage.rules
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow get;
allow create: if true;
}
}
}
As you can see, both rules allow all reads and writes.
ATTENTION: Do not use these rules in production, as they would allow everybody to read, write and delete data.
Step 2: Setting up your Dockerfile
Next up you'll need a Dockerfile
that specifies how to build your Firebase Emulator Docker image. Here's a simple example to get you started:
# We chose node-18 since currently firebase functions work on node-18 only and is in beta in node-20
# https://firebase.google.com/docs/functions/manage-functions#set_nodejs_version
FROM node:18-alpine
# Install JDK
RUN apk add --no-cache openjdk11
# Install the firebase cli
RUN npm install -g firebase-tools
# Clear the npm cache to keep the image tidy
RUN npm cache clean --force
# Install and setup all the Firebase emulators
RUN firebase setup:emulators:database
RUN firebase setup:emulators:firestore
RUN firebase setup:emulators:storage
RUN firebase setup:emulators:ui
# Copying everything (=the firebase configuration) to the workdir
COPY . /firebase
# Mount your firebase project directory to /firebase when running the container
# This is the folder containing the firebase.json
WORKDIR /firebase
# Expose the emulator ports
# If you use non standard ports change them to the ones in your firebase.json
# AUTH STORE RLTDB UI
EXPOSE 9099 9199 8080 4000
# Start the emulator
# we use a shell script to run the emulator to be able to pass in the project from the outside as environment variable
# As you can see, the script exports and imports the emulator data during shutdown and startup, which persists the existing data.
CMD ["sh", "-c", "firebase emulators:start --import=/firebase/data/ --export-on-exit=/firebase/data/ --project=${FB_PROJECT_ID}"]
This Dockerfile
does the following:
Starts with a Node.js image since Firebase CLI is a Node.js package.
Sets a working directory inside the container.
Copies your Firebase project files into the container.
Installs the Firebase CLI globally within the image.
Exposes the ports used by various Firebase services.
Defines a command to start the Firebase Emulator. As you can see, the script exports and imports the emulator data during shutdown and startup, which persists the existing data.
Step 3: Building your Docker image (optional)
With your Dockerfile
ready, navigate to the directory containing it and run:
docker build -t firebase-emulator .
This command builds your Docker image, tagging it as firebase-emulator
.
Step 4: Running your Firebase Emulator Docker container
Option 1: using docker run
ℹ️ For this option, Step 3 needs to be excuted
After building the image, start the Firebase Emulator by running:
docker run -d \
--name firebase-emulator \
-v firebase_data:/firebase \
-e FB_PROJECT_ID=[your_project_id] \
firebase-emulator
This command runs your Docker container, mapping the necessary ports from the container to your host machine, allowing you to access the Firebase Emulator services locally.
Option 2: using docker-compose
ℹ️ For this option, Step 3 is NOT necessary
You can also run your container on demand with docker-compose. For that create a docker-compose.yml
-file: touch docker-compose.yml
in the root of your project (next to the Dockerfile) and add these contents:
version: "3.1"
services:
firebase-emulator:
container_name: firebase-emulator
build:
context: . # <- This instructs docker to build the container from the current directory
dockerfile: Dockerfile
restart: always
ports:
- 4000:4000 #Emulator UI
- 9099:9099 #Firebase Auth
- 9199:9199 #Firebase Cloud Storage
- 9000:9000 #Firebase Realtime database
environment:
- FB_PROJECT_ID=[YOUR_PROJECT_ID] # Enter your Firebase project ID here
volumes:
- firebase_data:/firebase
volumes:
firebase_data:
Then run docker-compose up
to start the container. You can add -d
to the command to run the container in the background.
Using my Pre-built Docker image
While following the steps above provides a great learning experience, I've prepared a Docker image that encapsulates all these steps, saving you time and effort. You can find the Docker image on DockerHub at evolutecx/firebase-emulator
and the source code on GitHub at evolute-cx/firebase-emulator-docker
.
Using this pre-built image, you can start the Firebase Emulator with a single command:
docker run -d \
--name firebase-emulator \
-v firebase_data:/firebase \
-e FB_PROJECT_ID=[your_project_id] \
evolutecx/firebase-emulator:latest
Persisting data
The container is configured in a way that it exports all data to /firebase/data on shutdown and re-imports it from there during startup.
In order to persist data between runs, a named volume is needed.
IMPORTANT: It has to be a named volume, not a bind mount. A bind mount will not work because the host always takes precedence over the container and will overwrite the contents in /firebase which we dont want, because that's where all the config is residing too. bind-mounting to /firebase/data will also not work because firebase will complain about the destination existing already. So a named volume is the way to go (for now)
Source & Contribution
Everything we covered above is already part of our Github-repo here: https://github.com/evolute-cx/firebase-emulator-docker
Feel free to clone the source code and make adjustments to it, we can't wait to have a look at your PR ❤️
Conclusion
Dockerizing the Firebase Emulator streamlines your development workflow, offering a consistent, simple, and portable way to develop and test your Firebase applications. By following this guide, you can set up your Firebase Emulator in Docker from scratch or leverage the pre-built image I've provided to jumpstart your development process.
Integrating Docker and Firebase using this approach can significantly enhance your development lifecycle. I encourage you to explore this setup further and tailor it to your project's needs. Feel free to clone our repo and submit PRs to it.
Happy coding!