Having already described how I came up with the idea for a micro SaaS, I’d like to go into a little more detail about the technology used. According to the contract, the project should be validated first, but as a developer, I put a lot of thought into what development and production environment I wanted to use to make the deployment and hosting process as easy as possible.
Some problems of poorly tuned development and production environments
If you’ve worked on multiple monolithic projects, or if you’ve worked on one monolithic project over a period of years, you’re probably familiar with some of these issues from your own experience.
Missing libraries on the production system
There have been a few times when I have developed features and only after deploying them realized that certain libraries were missing on the production server. These were already present in the development environment because they were installed for other projects.
Project dependent packages are only available for Linux, but not for Windows
Since most web applications run on Linux-based systems, most web projects are developed primarily for Linux-based systems. I often found that I had found a suitable package for a problem, but could not use it in my development environment.
You work on multiple projects with different dependencies and version numbers
Each project has its own peculiarities and dependencies. Starting with the web server (Apache or NGINX), the version of the executing script language (PHP version, Python version, Node version) and of course the version numbers of the libraries installed via php composer, python pip or Node npm. Finally, there are dependencies on services such as databases.
As soon as you start switching between projects locally on your development machine, the different version numbers come into conflict with the production system.
Already used ports
If you want to run similar or identical services for different projects that need to be run separately depending on the project, you get the idea of assigning different ports to access them. This quickly gets confusing.
Switching from one project to another is time-consuming
Whether it is multiple projects being worked on in parallel, or a project that needs to be relaunched for development. It is frustrating when the change requires additional effort.
Requirements for the development and production environment
The following requirements result from the problems encountered.
- The development and production environments should be as identical as possible.
- All dependencies of a project should run in isolation from the dependencies of other projects.
- Multiple projects should run concurrently in the development environment without interfering with each other.
Docker as the lowest common denominator
As a developer, it is almost impossible not to have heard of Docker. So I won’t write much about it. Of course, containerization is the first approach to isolating dependencies and keeping the development environment as identical as possible to the production environment. So the question is not whether to use Docker, but how to organize your projects in Docker.
Kubernetes or Docker Swarm as a runtime environment?
As is often the case, there are solutions to this problem. In our case it is Kubernetes or Docker Swarm. Unfortunately, these solutions are intended for larger projects that are distributed across multiple servers.
Most projects, especially when developing something new, consist in the vast majority of cases of a web server, the developed application and a database. In the early days, it also resides on a server that may host other projects with a similar structure.
Kubernetes and Co. bring too much unnecessary complexity for this use case and cannot play to their strengths (distributed system). However, if you already have experience with Kubernetes, you should not be held back and take advantage of the possibilities.
The solution I chose to start with only works with docker and docker compose.
The dream team Traefik and OpenSSH
The tools used are Traefik and OpenSSH. Traefik acts as a proxy for HTTP/S traffic. This means that Traefik listens on ports 80 and 443. All other containers from the individual projects that also depend on these ports tell Traefik which hostname to visit. You can see exactly how to use Traefik in this video by Christian Lempa, for example.
OpenSSH also runs in a container and acts as an SSH tunnel for the databases. I mainly use it in the development environment. This way I can connect to the database with my IDE or database GUI without having to publish cryptic ports. The instructions I followed for this are from Peter Packet.
Traefik is also used productively in my company. This means that the configuration of the development and production environment of each project is only slightly different. These different configuration values all end up in one .env file. This has huge advantages when hosting new projects. Setting up a new project consists of four main steps
- Set domain DNS A-records (if necessary).
- Run git clone project-repository on server.
- Customize the .env file.
- Run docker compose up
Done. Admire the project on the public Internet.
With this solution I can develop and host any web application. The limited use of Plesk or cPanel is unnecessary. Because with the described approach, any Docker container (monitoring tools, marketing tools or tracking tools) can be launched and set to a hostname.
With a prepared base system of the application, development can start directly without having to worry about hosting and setup.
The cost of a small V-Server is as low as running a single container continuously on Google Cloud or AWS. (Continuous operation is especially necessary to avoid the “cold start” problem). With the difference that not only multiple containers but also multiple projects can be hosted on a single small V-Server.