Week 11

Deployment Strategies

Creating Docker Images

Multi-stage builds

Docker Compose

Container Registries

Deploying to the Cloud

Database Migrations

Practice

Assignment

Backend Track

Going live

Everything this week has been building to this chapter. You have an image that runs your whole backend, and a promise from the Docker Compose chapter to keep: in the cloud, your app becomes one deployed container that connects to a managed database service. The Compose file stays on your laptop.

This is what you are about to build:

image.png

At the end of this chapter, your API service answers requests from anyone, anywhere on the internet.

Choosing a platform

You could rent a virtual machine, install Docker on it, and run your container yourself. That is a real option, and now you understand every step it would involve. But then you are responsible for the server: updates, HTTPS certificates, restarting crashed apps at 3 AM.

A PaaS (Platform as a Service) takes that over: you bring the container, the platform brings servers, HTTPS, restarts and a public URL. That is the right trade for us — this track is about your application, not about server administration.

We chose to use a simple and free stack:

<aside> 💭

Railway, Fly.io, DigitalOcean, and the big clouds (AWS, Azure, GCP) all offer the same thing with different dashboards. Learn the concepts here and you can switch platforms in an afternoon.

</aside>

Step 1: The database on Neon

  1. Sign up at neon.com (you can log in with GitHub).
  2. Create a project — this gives you a running PostgreSQL database.
  3. Open the SQL Editor in the Neon console and run your schema SQL to create your tables in the cloud.
  4. Click Connect and copy the connection string. It contains everything: user, password, host and database name.
  5. [Optional] Test the connection from your computer by connecting to the neon DB with a GUI tool.

Neon will show a full connection URL, you may need to extract the credentials from there separately. Here is the format of the full connection URL:

postgresql://<USERNAME>:<PASSWORD>@<HOSTNAME>/<DB_NAME>?sslmode=require&channel_binding=require

Step 2: The app on Render

  1. Push your project including the Dockerfile and .dockerignore to a GitHub repository.
  2. Sign up at render.com and choose New → Web Service.
  3. Connect the GitHub repository. Render detects your Dockerfile and selects the Docker runtime.
  4. Pick the Frankfurt region (closest to the Netherlands) and the Free instance type.
  5. Add the environment variables from Step 3 below, then create the service.

Now watch the logs: Render runs your multi-stage docker build on its own machines and starts the container. From now on, every push to your branch triggers a new build and deploy - how cool is that?

Step 3: Configuration and secrets

Your deployed container needs to know where the database is. Same mechanism as in the Compose chapter — SPRING_DATASOURCE_* environment variables override application.yaml — but the values now point at Neon, and they live in Render's Environment tab.

One conversion trips everyone up. Neon gives you one Postgres-style string; Spring wants a JDBC URL plus separate credentials:

Neon gives you:


postgresql://hyf:[email protected]/neondb?sslmode=require

You configure in Render:


SPRING_DATASOURCE_URL      = jdbc:postgresql://ep-xxx.eu-central-1.aws.neon.tech/neondb?sslmode=require
SPRING_DATASOURCE_USERNAME = hyf
SPRING_DATASOURCE_PASSWORD = password
PORT                       = 8080

So: add jdbc: in front, move the user and password out of the URL into their own variables, and keep ?sslmode=require — Neon only accepts encrypted connections.

The extra PORT variable tells Render which port your container listens on. Render routes public traffic to the port named in PORT; our Spring Boot app listens on 8080.

<aside> ❗

This dashboard is where production secrets live — not in Git, not in the image, not in application.yaml. This is why we never hardcoded credentials all week. Your properties file says localhost, Compose said db, Render says Neon: three environments, one unchanged image.

</aside>

It is alive

When the deploy turns Live, your service has a public URL: https://<your-service>.onrender.com. Open an endpoint in the browser, fire your Postman collection at it — and then open it on your phone. Not localhost. The internet.

<aside> 🎉

Congratulations - this is a big milestone! Your project is now live 24/7 in the cloud and can be accessed by anyone around the world.

</aside>

Health checks

How does Render know your app is actually working — not crashed, not still starting? It asks. A health check is an endpoint the platform calls regularly; while it fails, the platform holds traffic back or restarts the container.

You can write one with what you already know:

@RestController
public class HealthController {

    @GetMapping("/health")
    public String health() {
        return "OK";
    }
}

Then, in your Render service settings, set the Health Check Path to /health. Render now probes it and only considers a deploy successful once it answers.

<aside> 💡

In real Java projects this endpoint usually comes from Spring Boot Actuator, a standard module that provides /actuator/health including database checks. You will meet it on the job — and now you know exactly what it does under the hood.

</aside>

Reading logs in the cloud

The Logs tab of your Render service is docker logs for the cloud: build output, Spring Boot startup, every request, every stack trace — streamed live.

The debugging reflex stays the same as it was locally: deploy failed, app crashed, endpoint misbehaves? Logs first. The reason is almost always in there — a typo in an environment variable, a failed database connection, an exception at startup.

The free tier limitations

  1. Free services sleep. Render spins your service down after 15 minutes without traffic, and the next request waits up to a minute while it wakes. Neon's free database also pauses when idle and wakes on the first query. So: the first request after a quiet period is slow — that is not a bug in your app. Presenting your project to someone? Open the URL a minute before the demo to warm it up.
  2. The free tier has no persistent disk: anything your app writes to its filesystem disappears on every restart or deploy. Data belongs in the database — and files belong in object storage, which you met in Week 9.

Extra resources


The HackYourFuture curriculum is licensed under CC BY-NC-SA 4.0 *https://hackyourfuture.net/*

CC BY-NC-SA 4.0 Icons

Built with ❤️ by the HackYourFuture community · Thank you, contributors

Found a mistake or have a suggestion? Let us know in the feedback form.