All Articles

AI Made Building Apps Easier, but Hosting Them Became More Expensive

Abbas

•6 min read

Building more applications using AI has increased my hosting costs. I started with a cheap VPS, but as I built more applications, I had to purchase additional VPS instances. Managing multiple servers increased my expenses and introduced new challenges. This pushed me to rethink how I host my applications.

I'm not a DevOps expert. I've just learned what I needed through experience over the past few years. In this article, I want to share how my approach has evolved.

Now, with AI making it easier to build applications, having a simple way to deploy them is even more valuable.

How I Got Here

If you are not interested in the background or history, you can skip this section. My hosting approach has changed through several stages, each introducing new problems and lessons.

1. Starting with a Simple Backend

At the beginning, I only knew how to build backend APIs (I didn't know how to build frontend applications), so I mostly used Android as a client. At that time, I just bought a cheap VPS, ran my backend on it, opened the required ports, and connected my Android application to it.

Initially, I ran the backend directly, for example, a Go application. Later, I started using systemd and eventually Docker Compose because I wanted to run databases and other services more easily.

2. Adding Frontends and Discovering Caddy

Then I started using AI to build frontend applications, so I had to host both frontend and backend for each application.

I continued using cheap VPS servers, exposing the frontend and backend through different ports and accessing them using the server's IP address.

After a while, I realized this wasn't a good approach. I had to remember different IP addresses and ports for different services.

That's when I learned about reverse proxies.

After some research, I chose Caddy. Instead of exposing multiple public ports, I could use Caddy to receive requests on ports 80 and 443 and forward them to the correct frontend or backend based on the domain or request path. It was simple, and Caddy also handled HTTPS automatically.

3. Moving Multiple Applications to One VPS

The next problem was cost. I had multiple VPS servers, each running only one application. Even though they were cheap individually, the total monthly cost kept increasing as I built more applications.

So I decided to move multiple applications to one VPS. That's when I faced two new challenges.

The first was port conflicts. For example, two different applications might both use port 8080. On separate VPS servers, that wasn’t a problem. However, on the same VPS, apps couldn't run because another app was already using that port. So every time I deployed a new project, I needed to check which ports other projects were already using.

The second problem was managing Caddy configuration. I wasn't sure where to keep Caddy and its Caddyfile. Should they belong to one of my projects or have their own repository? When adding a new application, where should I make and commit the changes?

Using a Shared Docker Network and Caddy

Image

My goal was to develop and deploy each project independently without worrying about the others. Eventually, I learned that Docker supports creating a shared network and connecting multiple independent Docker Compose projects to it. This solved most of my problems.

Here's a simplified example of how I connect independent Docker Compose projects through a shared network. First, we need to create a shared network:

text
docker network create shared

In the application's compose.yaml, each application should be connected to the network:

text
services:
  web:
    image: my-app
    networks:
      shared:
        aliases:
          - project-a-frontend

networks:
  shared:
    external: true

The Caddy container also needs to be connected to the same shared network. Then, in the central Caddyfile, we can route requests to each application using its network alias and the internal port it listens on. Here, 3000 is the port the application listens on inside its container, not a published host port.

text
app.example.com {
    reverse_proxy project-a-frontend:3000
}

Now, each application has its own Docker Compose file and can be deployed independently. Services don't need to publish their ports on the host because Caddy can reach them through the shared network.

Because they're running in separate containers, multiple applications can use the same internal port without conflicts.

Then I created a separate Docker Compose project just for Caddy and connected it to the same Docker network.

Caddy is the only service that publishes ports 80 and 443 on the host. It forwards requests to each application using its service name or a unique network alias and internal port.

For example, Caddy can forward requests to project-a-frontend:3000 or project-b-backend:8080.

I keep all these projects in one folder called stack.

text
stack/
├── infra/
│   ├── compose.yaml
│   └── Caddyfile
├── project-a/
│   └── compose.yaml
├── project-b/
│   └── compose.yaml
└── project-c/
    └── compose.yaml

Inside it, I have an infra folder for Caddy and separate folders for each application, each with its own Docker Compose configuration.

Now I can deploy a new application without worrying about port conflicts or modifying other application projects. I only need to update Caddy's configuration when adding a new domain or route. Instead of paying for multiple cheap VPS servers, I now use one VPS with more RAM and CPU and deploy multiple projects on it.

Simplifying Deployment with GitLab

To keep deployments simple, I use GitLab.

I push my changes from my local machine to GitLab. Then, on the VPS, I navigate to the project's folder, run git pull, and rebuild the containers using Docker Compose.

That's it. I know there are more advanced solutions, like CI/CD pipelines and container orchestration tools, but I wanted to keep everything simple

Next Steps

Recently, I started using AI to make deployments easier. I asked my coding agent to create shell scripts for deployment tasks. I also created some deployment skills that the agent can use.

I think the next step is to have a CLI tool or MCP on the VPS return the current state of services and folders to the agent. Then, the agent can use that information and the skills I've already provided to handle deployments. This is what I want to work on next.

However, I’m not saying this is the best approach for everyone. Running multiple applications on one VPS has its downsides, especially if the server goes down or if one application uses too many resources. But for my personal projects, it works well. I can build new applications and deploy them independently while keeping infrastructure costs relatively low. Now, with AI making it easier to build applications, having a simple way to deploy them is even more valuable.

Perspectives

0 voices

No Perspectives yet — be the first to share one.

Share your perspective
Abbas Thoughts
AI Made Building Apps Easier, but Hosting Them Became More Expensive — Layered