Appsmith's Deployment Architecture
Tags: docker, opensource, systemdesign
Published: 2023-06-14 16:01:57

Appsmith is a turnkey solution to building internal apps in the cloud. It’s a [complete workbench](https://www.appsmith.com/use-cases) for building graphical interfaces and tools that connect to your existing data sources, and it [integrates with popular third-party services](https://www.appsmith.com/integration) for communication, collaboration, and commerce. Appsmith is also the community behind this project, comprising the company that invests in and guides the overarching development of the platform, the open-source contributors who help us ideate and implement, and our end users. 

This article serves as an introduction to the Appsmith platform for technical readers who want to get to know how Appsmith works under the hood by examining the architecture behind our deployment binary and the problems this architecture choice solves. Whether you're looking to contribute to the Appsmith platform, wanting to build with it, or just interested in the technical considerations you might need to make for your own projects, this article will give you a solid understanding of the platform.

## Appsmith’s open-source philosophy and modular architecture

One of the major contributing factors to the structure of our codebase is the open-source nature of Appsmith. We think that open source is the best way to make software that naturally evolves to meet its community's needs for three primary reasons:

* **Code quality**: With more eyes on the code, the quality is greatly improved. Developers are encouraged to be more conscientious about their code, and mistakes that do make it into the repository can be spotted by others and fixed before they enter production.
* **A strong ecosystem**: Community suggestions and contributions mean that our platform is always on the leading edge, responding to ever-evolving user requirements for functionality and integration.
* **Longevity**: For the two reasons above, open-source projects are also durable — the platform and ecosystem surrounding them are public property to which access cannot be revoked, making them a safe investment for organizations to base their toolchains and workflows on.

A project as complex as Appsmith must make architectural considerations to ensure that the project can be contained within a single manageable codebase. Teams and contributors need to be able to work on different features without stepping on each other's toes, especially when they are spread across the globe. 

To address this, our codebase is well documented and heavily modular, with a strict hierarchy of directories and ownership, all contained within a single monolithic repository. This concept is reflected in our deployment architecture — a single container comprising all of the moving parts to make Appsmith work.

## What does Appsmith’s deployment architecture look like?

Appsmith is based on industry-standard technologies to provide front-end and back-end functionality organized in a single codebase. We deploy as a single Docker container which encapsulates:

1. The Appsmith Java back-end process
2. The NGINX server
3. The real-time service (RTS)
4. The MongoDB server
5. The Redis server

![imageone](//images.ctfassets.net/lpvian6u6i39/3yQAGwqhkcSOQc32GnA4ha/bb7f33604f6e5e8dabed7125f4a50661/imageone.png)
_Appsmith’s components and architecture_

We call this the “modular monolithic” architecture. A single Docker container powers the whole application, making it easy to install. All dependencies inside the container are under our control, which makes updates easy to implement for developers and more reliable for end users.

## The deployment challenges this architecture choice solves

Appsmith wasn't always shipped as a single binary. Originally we shipped each component individually — one for the Java backend process, one for NGINX, one for MongoDB, etc. — totalling five Docker containers that you had to install to get up and running. 

This lead to several problems for the end user, which then became problems for our developers to solve:

### Problem #1: Complexity

Running a separate container for each piece of technology that underpins Appsmith made sense initially: each was a standalone program, so it followed that it should have its own container. This made deployments complex: the Java back end needed to talk to MongoDB and Redis, NGINX needed to talk to the back end, and the RTS needed to talk to the backend and NGINX. Installation wasn't just a matter of deploying containers; they all needed to be configured to communicate with each other.

With this multi-container architecture, our early users only had a 30% success rate on their installations. This deployment mess would also only grow with time if we decided to swap out or add components to the Appsmith platform.

### Problem #2: Compatibility and upgrades

Running on outdated dependencies would be to the detriment of Appsmith — we want to be compatible with the latest databases and leverage the performance and functionality gains from the open-source projects we’ve built on. With a multi-container deployment architecture, every time we updated one of our components (for example, moving to the next major release of MongoDB), our users would have to manually update their `docker-compose.yml` file, redeploy, and hope that nothing breaks. If we updated all of our components, this would mean upgrading five containers.

![imagetwo](//images.ctfassets.net/lpvian6u6i39/18rayGXKXo1yeMXZT2qoSo/c1e82cfb1078f0a05d87558220911ea8/imagetwo.png)
_Keeping multi-container deployments up and running can be an annoying task_

It was unrealistic to document the changes users would have to make to their Docker configuration and expect them to follow through with them to remain up to date. We could see immediately that it would result in many users never updating and some even spending that effort migrating to a platform with a more reliable and predictable upgrade experience instead.

### Problem #3: Bash scripts are unintuitive

In an attempt to smooth out the multi-container deployment and upgrade process, we wrote a shell script, unimaginatively called `install.sh`. It would ask you a sequence of questions about your environment and generate a `docker-compose.yml` file. Running this file would configure the multiple Docker containers required to run Appsmith and set up all of the connections between them for you.

While `install.sh` worked on a technical level and was an elegant piece of scripting, it did not work on a user-experience level.

Bash scripts for deployment do not inspire confidence. Users rightfully see them as a risk, and they give the impression of being a “hacky” solution — no matter how elegant they are. There is also the risk that inconsistencies in user environments could lead to unintended side effects from a Bash script (not everyone reads through them before running them). Something as simple as a user storing files at an unexpected path could result in accidental overwrite or deletion.

The other issue we foresaw with Bash scripts, especially as our user base grew, was a lack of control. Updates or changes to a user's system could require manual maintenance to keep Appsmith’s containers properly configured and communicating. This, again, would only become a bigger mess as time went on and we upgraded or replaced different Appsmith modules (for example, upgrading the version of MongoDB or swapping Redis for a different ephemeral data store). It would be almost impossible to keep our Bash script updated for every encountered deployment scenario and allow for consistent updates from different prior releases.

Collectively, these problems were a significant barrier to entry for any user looking at Appsmith. A solution was decided on — consolidating to a single container that was easy to install, easy to upgrade, and not reliant on free-roaming scripts being run on the user’s system.

## The decision to implement a single-container architecture, and how we did it

Our decision to deploy a ”fat binary” — a single Docker container comprising all of the Appsmith code and dependencies — comes from both the need to meet our users’ expectations for a smooth deployment experience and our own frustrations with overly-simplistic hosting paradigms, which, ironically, are often implemented to replace similarly frustrating complicated setup processes.

Over-simplified installation processes do not allow for user choice around privacy, security, and performance. Users need to be able to specify how and where data is stored, choose what information is made available to other systems, and make decisions about the performance (and resulting costs) incurred by their infrastructure. Removing these choices in favor of a quick start is not a workable trade-off — there needs to be a balance that offers users a streamlined installation experience, with the option for granular configuration.

It’s apparent from user feedback that our current solution achieves this, but it does fly against convention, so we had to make sure our decision to implement it was fully informed, justified, and tested.

### Reconsidering Docker conventions

When containerizing an application using Docker, it is generally considered best practice that each process needed for the application be run as a separate container, then connected to others as required. As discussed, this would currently require five total containers for Appsmith and its dependencies to run.

Our single-container solution breaks this convention. While the prevalent perception is that Docker works best when running and managing one single process, we found that the advantages of delivering all of our code in a single container outweighed any potential disadvantages. This decision was not taken lightly. We did not want to compromise the performance or stability of the platform in production, so we fully researched and tested the implications of deploying to a single container, adopting the credo:

> _You can break the rules as long as you know why they were made._

If you fully understand why a rule or best practice exists, your justification for breaking it is fully informed. Every problem is different, and blanket rules shouldn’t prevent a practical, reliable, and secure solution. 

Our goal was to abstract work away from users who had no requirement for extra customization, like hosting their own databases and services to support their Appsmith deployments. We broke the rule because, after considering the [reasons for the rule](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#decouple-applications), we found that they didn't apply to our use case. Unlike the majority of Docker usage scenarios we do not need to scale and monitor services independently or segregate services for security, so we decided the fat container approach was appropriate. Additionally, [GitLab does the same thing](https://docs.gitlab.com/ee/install/docker.html) and we thought,_ if it's good enough for GitLab, it's good enough for us!_

This meant that instead of using Docker to isolate processes, we used it to isolate the whole application, repurposing Docker to provide a single executable binary for end-user convenience rather than process isolation for DevOps purposes.

### Maintaining attention to detail

This design doesn’t exclude users who do want to host their own MongoDB, Redis, or other services and connect Appsmith to them. The Appsmith container is smart enough to recognize when an external MongoDB or Redis server is configured and doesn’t start either process inside the Appsmith Docker container.

> _We are developers first, building a developer-first experience at Appsmith._

-- Quote from the Appsmith development team

This “smart” behavior is a result of our attention to detail. We want users to delight in the small quality-of-life features we have included in Appsmith — features that we’ve added to address our own day-to-day inconveniences and that we know will be appreciated by other developers. We didn’t want an architecture that would limit the ways that we could extend the platform in the future, whether at its foundations (affecting deployment) or higher up (affecting the end-user experience).

An example of one of these quality-of-life features that affects both those deploying and maintaining Appsmith and those using it is our implementation of SSL. When you configure a custom domain for your instance, Appsmith will attempt to provision an SSL certificate from Let’s Encrypt. If it succeeds, the instance will start with HTTPS enabled on the web server, and redirect all requests with `http://` URLs to `https://`. If the Let's Encrypt provisioning fails, the instance starts with plain HTTP, without displaying worrying errors or browser warnings that users may be encouraged to ignore, setting dangerous precedents.

Again, in keeping with our philosophy of making setup as simple as possible but still giving advanced users full control of their infrastructure, it is also possible to [supply your own custom SSL certificates](https://docs.appsmith.com/getting-started/setup/instance-configuration/custom-domain#configure-custom-ssl).

[Appsmithctl](https://docs.appsmith.com/getting-started/setup/instance-management/appsmithctl) is another example of our user-centric features. It provides a convenient interface for managing and maintaining your Appsmith instance and allows you to back up all of your Appsmith data with a single command:

```
docker-compose exec appsmith appsmithctl backup
```

Regular backups are the most important practice for ensuring continuity for any organization that relies on their digital assets, so we wanted to make sure a programmatic way to do this was available out of the box.

### Implementing process management in the Appsmith container

![imagethree](//images.ctfassets.net/lpvian6u6i39/61DXLZdN9vlSOjPVCBvzVO/6faf3fe4243a20d100e65270684abd9d/imagethree.png)
_Supervisor’s web and CLI interfaces_

Moving everything into a single Docker container means we can't use Docker to monitor the status of each component. Instead, we use [Supervisor](http://supervisord.org/), a popular, lightweight process manager. This has proven effective — when given a list of things to run, it will run them, start them again if they crash, and collect their logs in case further troubleshooting is required.

Supervisor comes with a web interface with a graphical view of all the processes it’s managing. [We expose this interface](https://docs.appsmith.com/getting-started/setup/instance-management/supervisor), so you can see what processes Appsmith has launched internally, monitor performance and resource usage, and even restart services manually if something gets stuck.

All of the features exposed through the Supervisor web interface are also available on the command line from outside of the container. For example, the following command will list the status of each running process:

```
docker-compose exec appsmith supervisorctl status
```

This allows for programmatic access, useful for advanced users who may want to further orchestrate and automate the running of their Appsmith instances.

Process management with Supervisor works as follows: When the Appsmith container starts, the processes that need to start with it are identified. If external MongoDB or Redis databases are configured, those services inside the container will not start. The Java backend process, the Node.js RTS, and NGINX will always start. For each of these identified processes, the container's entry-point script — invoked by Docker when the container is started — generates a set of Supervisor configuration files containing only the services that need to run. The configuration for each service contains any environment variables required, the command to launch the service executable, and logging details.

Again, this illustrates how the move to a single container has not impacted user choice or control over the different components of Appsmith; it just abstracts decision-making away from the majority of users, who just want to start using the platform.

## The effects on deployment for end users

![imagefour](//images.ctfassets.net/lpvian6u6i39/713USgEK4VlazbcgoWjBS9/7b8af4ec2785086689b0f919708caa38/imagefour.png)
_A single container makes deployment fast and hassle-free_

**The proof is in the numbers: the abysmal 30% installation success rate for our multi-container deployment architecture has gone up to a 99% success rate with single-container Appsmith deployments.**

Appsmith is now a single Docker container that [installs with a single command](https://docs.appsmith.com/getting-started/setup/installation-guides/docker) (or a single click, if you're [deploying to the cloud](https://docs.appsmith.com/getting-started/setup/installation-guides)). Hosting and management complexities are largely hidden, so that you can focus on building your own apps, not trying to fix ours.

> _We loved that we could deploy Appsmith on our local instance and build an app. For us, it was an important concern because it would involve our production database credentials, which we were unwilling to share.
>
> It’s so easy for anyone to set up in the first place. It simply went directly to the AWS marketplace, selected their pre-existing supported AMI, and spun up an EC2. Instantly it was up and running._

-- Feedback from user

### Seamless updates

Updates are now seamless as well. [Optional automatic updates](https://docs.appsmith.com/getting-started/setup/instance-management/maintenance-window) can be enabled and will be installed during a specified maintenance window. Recently we upgraded to MongoDB v5 within our deployment container — and it was seamless for our end users. 

We can now update all of the components in Appsmith, and even add new microservices, with full confidence that we won't break anything on our users’ systems.

### Kubernetes support

The single-container approach has also made [supporting Appsmith on Kubernetes](https://docs.appsmith.com/getting-started/setup/installation-guides/kubernetes) much easier. This was a highly-demanded feature in our community, and the reasoning resonated with us well enough that we prioritized Kubernetes support.

One of the primary reasons for using Kubernetes (scalability aside) is rolling deployments. Containers can be dynamically upgraded, or deprovisioned and reprovisioned with updated code, with enough containers always online to meet current demand, resulting in zero downtime. 

An Appsmith upgrade can take up to 20 seconds  — fine for most users, but too long in high-availability scenarios — and can still fail in edge cases. With Kubernetes, nodes that fail to upgrade or become unresponsive are simply not used, and can be deprovisioned or troubleshooted. The single-container approach we have chosen makes managing an Appsmith Kubernetes cluster far easier than it would be if all of the components were hosted in separate containers, giving our users additional reliability through redundancy.

## How Appsmith strives to earn and maintain community trust

All of the above work is towards one goal — an easy to deploy, production-ready internal app studio that lets developers focus on their business logic and get on with their jobs. This requires a community providing their requirements and ideas, and that requires trust. At Appsmith we’ve worked hard to earn this trust with our users and open-source community. As demonstrated by our architecture decisions, we maintain this trust by ensuring we pay full attention to our core responsibilities and our communities needs:

### Security and openness

To ensure the security of your vital data, Appsmith can be hosted wherever you are comfortable hosting it — from the public cloud to your own VPC, or on-site. Our monolithic container lends itself to this perfectly.

Because we are open source, our code can be vetted by anyone, and we are [transparent about our security practices](https://docs.appsmith.com/product/security) so that our users are informed.

### Performance and simplicity

You don't have to run Appsmith on professional infrastructure. You can test and run it with full confidence in any supported Docker environment. Our team members use Appsmith hosted on our laptops, home servers, affordable cloud hosts, and even high-redundancy clusters. Wherever you are deploying Appsmith, we make sure that the process is as simple as possible.

### Reliability and control

Our open-source code is of high quality and is contributed to and reviewed by both our internal teams and community volunteers. We keep you in control of your Appsmith deployments, so that you can decide exactly where you want your data stored and whether you want automatic updates to run. This means you're never caught by surprise by unexpected changes or the loss of data stored in-container. **We take user control and choice seriously.**

## Appsmith: a rock-solid platform that we can all build on

When building an application, finding the balance between practices that benefit the developers and the end users can be difficult. Developers' decisions about the architecture and underlying technologies of an application can either expand or restrict the eventual feature possibilities of a platform.

This extends to deployment. Deploying Appsmith as a collection of connected microservices hosted in separate Docker containers, while beneficial during development and debugging, did not make for a pleasant or reliable experience for users running our tested application releases. Our move to a modular monolithic architecture and the further decision to deploy Appsmith as a single Docker container have improved both our developer and our end-user experience.

If you're a developer interested in contributing to the Appsmith open-source project, our [contribution guidelines](https://github.com/appsmithorg/appsmith/blob/release/contributions/CodeContributionsGuidelines.md) will get you started, and you can join us on [Discord](https://discord.com/invite/rBTTVJp). If you build internal apps and are interested in what Appsmith can do to streamline your own development process, our [tutorials](https://www.appsmith.com/blog?cat=Tutorial) and [user documentation](https://docs.appsmith.com/) will get you started.