A case study of Uffizzi-built ephemeral environments for databases, to solve the problems arising from the “shared development database” approach. Uffizzi ephemeral database environments, built on Kubernetes, allow consistency, and resilience, save cost, scale easily, eliminate friction in testing, and can be provisioned and destroyed in a couple of seconds.
After some months of analyzing development patterns across teams, we noted that most projects were following a shared database model for development and testing purposes. This is a troublesome paradigm because it does not play along with modern development practices like continuous integration and delivery. As features are being developed continuously, these must be tested continuously, too, in isolation, to make sure their impact is efficiently tracked. Projects sometimes have a couple of static dev/test environments (or perhaps even dedicated environments) for testing their application, but the problem is all these dev environments share the same database. All developers are using this database for testing and experimentation, defeating the purpose of testing in isolation. Read here about how this is an anti-pattern.
At first glance, this might look like a lot simpler approach, especially because running and maintaining databases require significant cost and effort, and constantly dumping and restoring databases requires a non-trivial amount of time. What could possibly go wrong with having one or two instances of Postgres Server — filled with data from production — that all the developers can use to develop and test the next version of the application with “prod-like” data?
The fact is, there are tons of hidden costs and developer performance penalties that come with adopting this approach over a dedicated, ephemeral database environment approach. Using a shared dev/test database model can work reasonably well for small teams, but once projects start to scale, this approach simply doesn’t work. Here are some of the problems of following a shared development database approach:
As mentioned earlier, these issues can be managed for small teams of 2–4 developers, but as projects start to grow, unwieldy dev and test shared database environments create bottlenecks for engineers who are always at risk of making changes that break the build and hence slows development, test and release cycles while increasing costs. Running development databases locally can alleviate some of these issues BUT it raises another set of problems, the most notable one being the lack of resources needed to run a DBMS locally.
The lack of dedicated, ephemeral test environments for databases is an anti-pattern, that makes following agile practices like continuous integration, development, and testing unmanageable. These engineering challenges introduce the following operational challenges:
To combat these rather common issues, faced by multiple development teams, Uffizzi built an ephemeral environment solution for databases.
Let’s visit the idea of ephemeral development databases through an example: I have a team of 12 developers, 6 of which work on the BE of the application. As my team builds features for this application, I would want them to test the features continuously in their dedicated environments to make sure what is being tested is not impacted by their teammates’ code. I would also want them to test how this new feature in my application would interact with our BE database. For example, my business logic has a certain way of handling duplicate entries. Will this new feature break that?
To run these integration tests between my application and the database it uses, I will need an isolated, dedicated ephemeral environment running my app with an isolated, fresh, ephemeral DB to guarantee that the DB my team is running tests against hasn’t been left in an unexpected state by previous changes. In a shared database model, chances are that the DB state a test relies on has been altered by changes made by another developer, which could lead to major flaws in the application.
Through this one example, we can already understand how the ephemeral development database model is the better alternative to the shared/communal model.
We had a hypothesis that the shared development database model is an anti-pattern and ephemeral databases are the way to go. We then built a prototype to conclusively establish our hypothesis.
Uffizzi built a Pull Request workflow prototype to run Postgres instances on ephemeral environments. These ephemeral environments for Postgres can instantiate a fresh Postgres environment for development and can be destroyed when no longer needed.
Typically, developers will be running unit tests on their applications and integration tests with their databases. To test this development pattern, Uffizzi built the solution to run pgAdmin in an ephemeral environment, where a PR to pgAdmin (replace with your application here) will create an ephemeral environment on Cloud, orchestrated through Kubernetes, with pgAdmin installed. The ephemeral environment will be started with an ephemeral Postgres instance that is dedicated to this pgAdmin instance and will be isolated from other environments.
This PR workflow allows teams to follow agile practices by integrating into existing CI/CD pipelines effortlessly. In this solution, the workflow is integrated into GitHub Actions— any PRs opened to the example repo will instantiate a new ephemeral environment with a fresh, ephemeral Postgres instance.
You can follow these steps to configure ephemeral development database environments for your project. You can also checkout the PoC on GitHub.
For this prototype solution, we forked pgAdmin, an open-source GUI client for administering and monitoring Postgres. Here, pgAdmin acts as a client, consuming and making changes to Postgres. After we make a change to this client and open a new PR in the repo, a new ephemeral environment is spun-up with our client (pgAdmin), updated with the new logic, and a fresh instance of Postgres to develop and run tests against.
We then containerized pgAdmin, so it is self-contained and can be orchestrated easily. The pgAdmin container is fully configured with all its needed dependencies. This allows for effortless updates and patches. pgAdmin already comes with a Dockerfile, which we used to build our container.
After containerizing the application, Uffizzi then uses the docker-compose.uffizzi.yml
file to define other containers (services) that the application is dependent on. This compose file is used to orchestrate the containers on Kubernetes. In our compose file, we defined two containers: Postgres and pgAdmin.
In the compose file, we define what images should be used to build the container. We use an official Postgres image and build pgAdmin from source. As mentioned previously, this allows developers to test changes to their application, as each time the ephemeral environment will be built with the new code changes to the app, and a fresh Postgres instance to develop and run tests against. More on this in the next step.
Other notable components of the docker-compose.uffizzi.yml file are:
The power of ephemeral environments lies within the realms of CI/CD. Uffizzi leveraged GitHub actions to orchestrate building the ephemeral environments, collecting feedback, updating the environments as the feature gets iterated on, and finally deleting the environment if it is no longer required.
We built a 2-stage GitHub workflow, to extend GHA-triggered ephemeral environments support for open-source projects. The 2 stages of this workflow are:
Running database servers can easily hike resource consumption and cost. If multiple databases had to be created for dev/test purposes, that would escalate the issue. This is where ephemeral databases provide great value because they allow you to follow the pay-as-you-go model; the uffizzi-preview.yml action helps achieve this model by deleting the environment and freeing the resources as the PR is closed. Particularly significant for resource-intensive applications like running databases, running simulated hardware, etc. Read this blog to see how we built a prototype for running embedded applications with simulated hardware in an ephemeral environment.
After adding the above 2 GitHub workflows in our fork of pgAdmin (if you’re following this example for your project, make sure the actions are added to the default branch of your repo, else the 2-stage workflow will not work as desired.) we opened a feature PR in this fork. This triggered the uffizzi-build.yml, which then triggered the uffizzi-preview.yml workflow and created a new pgAdmin instance with an ephemeral Postgres instance, seeded with data from the database initialization script we mounted onto the container.
As commits are made to PR, the pgAdmin instance will be refreshed, and persisting changes made to the Postgres instance between container restarts through the use of the volumes attribute in our compose. As the environment is refreshed with new commits, the state of Postgres would be the same as it was left last. This allows developers to continuously iterate on their features without constantly having to check the state of their database.
Every new PR creates a new instance of the application you’re building from source along with other containers you’ve defined in docker-compose.uffizzi.yml. In this case, a new PR rebuilt pgAdmin and each environment was started with a fresh instance of Postgres. If you want to persist data between container restarts (triggered by making new commits to the PR) for any container, you can use the volumes directive as we did for Postgres.
The environment will be destroyed right when the PR is closed. This is a huge time and cost-saver since it conserves the resources that would otherwise be used to manage unwieldy dev or test database environments.
We ran Postgres locally, in a shared environment, and on ephemeral test environments and used pgAdmin as a client to Postgres, to administer and monitor our Postgres instances. Below, we are listing the prominent results of running Postgres on ephemeral environments and the value this added to development processes by providing fully-configured, consistent, isolated, cheap, and fast ephemeral database environments, as compared to a local or a shared development model.
By running this prototype, we conclusively established that developing with ephemeral database environments solves all sorts of problems engendered by the communal DB model. These ephemeral database environments bring your database into modern development practices, which facilitate innovation, allow you reach to the market faster, and reduce costs.
To read out more about Uffizzi and our ephemeral environment solutions,, check us out at Uffizzi cloud. If you’re also looking for cheap, isolated, and fast development/test environments, connect with our team and accelerate your development, testing, and release cycles.