Recommended resources
The freely available resources this lesson will direct you to.
Hello! Welcome to the next lesson in our course.
In our last session, we constructed a production-ready, multi-stage Dockerfile for our Tanstack Start application using the Bun runtime. We now have a portable, consistent, and optimized container image that packages our application and its dependencies.
Today, we will take that container and bring it to life on the web. The learning outcome for this lesson is to deploy the containerized Bun application to a suitable hosting platform (e.g., Fly.io). We'll use the Dockerfile we designed and leverage the Fly.io platform to host our SSR application, moving from a local artifact to a publicly accessible service.
This lesson bridges the gap between local development and a live production environment, a critical step in your goal of mastering modern SSR application deployment.
1. Introduction to Fly.io: A Home for Containers
While platforms like Vercel excel at serverless deployments from a Git repository, Fly.io specializes in a different model: running application containers close to users. Its philosophy is simple and powerful: "If you can build it into a Docker file, we can run it."
This makes it an excellent choice for our application, as we've already done the work of containerizing it.
To get started, you will need to:
- Create a Fly.io account. You can sign in with your GitHub account. As noted in the video we'll watch, Fly.io may require a credit card on file to activate the free tier, though you won't be charged as long as you stay within the free resource limits.
- Install the Fly.io CLI,
flyctl. Follow the instructions on their website for your operating system (e.g.,brew install flyctlon macOS).
Once you have flyctl installed, you can authenticate by running fly auth login.
2. The Deployment Workflow
The core Fly.io workflow is driven by the flyctl CLI and is remarkably straightforward. It consists of two main commands: fly launch for initial setup and fly deploy for subsequent deployments.
Let's watch a video that demonstrates this process with a Bun application.
Deploy a Bun API in Under 5 Minutes - Fly.io & Railway
The video 'Deploy a Bun API in Under 5 Minutes' by CodeBrew provides a concise walkthrough of deploying a Bun application to Fly.io. It clearly shows the key steps we're about to perform.
Please watch the section on deploying to Fly.io (01:05 - 03:07). Pay close attention to the sequence of commands (fly launch, fly deploy) and the files that are automatically generated (fly.toml, Dockerfile). Note that while the video shows Fly.io generating a Dockerfile, we will be using the one we created in the previous lesson.
Now, let's break down that process and apply it to our Tanstack Start project.
Step 1: fly launch
Navigate to your project's root directory in your terminal—the one containing the Dockerfile from our previous lesson. Run the following command:
fly launch
This interactive command is the starting point for any new application on Fly.io. It will:
- Scan your directory: It detects the
Dockerfile, confirming that you have a containerized application. - Ask for configuration details:
- App Name: Choose a globally unique name for your application.
- Region: Select a primary region to deploy your app. Choose one geographically close to you for lower latency during management.
- PostgreSQL/Redis: It will offer to provision a database or Redis instance. For our current application, we don't need one, so you can decline these offers.
- Generate
fly.toml: It creates a configuration file in your project root. This file defines everything about how your app runs on Fly.io. - Offer to deploy: It will ask if you want to deploy immediately. You can say yes, or say no to inspect the configuration first.
Step 2: Understanding the fly.toml File
The fly.toml file is the blueprint for your application on Fly.io. The generated file will look something like this:
# fly.toml
app = 'your-tanstack-app-name'
primary_region = 'iad' # e.g., Ashburn, Virginia (US)
[build]
# This tells Fly.io to use the local Dockerfile to build the image.
dockerfile = 'Dockerfile'
[[services]]
protocol = 'tcp'
internal_port = 3000 # The port our app listens on INSIDE the container (from EXPOSE in Dockerfile)
processes = ['app']
[[services.ports]]
port = 80
handlers = ['http']
force_https = true
[[services.ports]]
port = 443
handlers = ['tls', 'http']
[services.concurrency]
type = 'connections'
hard_limit = 25
soft_limit = 20
Key Sections:
appandprimary_region: Basic application identifiers.[build]: Specifies how to build your app. Since we have aDockerfile, it defaults to using that.[[services]]: This is the most important section. It defines how network traffic reaches your application.internal_port = 3000: This tells Fly.io that our application process inside the container is listening on port 3000. This must match theEXPOSEport in yourDockerfileand the port yourserver.tslistens on.services.ports: These stanzas map external public ports (80 for HTTP, 443 for HTTPS) to yourinternal_port.force_https = trueautomatically redirects HTTP traffic to HTTPS.
Step 3: Setting Environment Variables
Your production application will inevitably need secrets, such as API keys or database connection strings. You should never hardcode these in your Dockerfile or fly.toml. Instead, use Fly.io's secrets management.
To set a secret, you run:
fly secrets set MY_API_KEY="some_super_secret_value"
These secrets are securely injected as environment variables into your running container at runtime. You can list existing secrets with fly secrets list.
Step 4: fly deploy
With your configuration in place, deploying is a single command:
fly deploy
This command initiates the following sequence:
- It archives your project directory (respecting
.dockerignore) and sends this "build context" to a remote builder VM on Fly.io. - The remote builder executes the
docker buildcommand using yourDockerfile. - Once the image is successfully built, Fly.io provisions a Firecracker MicroVM.
- The newly built container image is started on the VM, and once it passes health checks, it begins receiving traffic.
3. Verifying and Managing Your Deployment
After the fly deploy command completes, your application is live. You can manage and inspect it with several flyctl commands:
fly status: Shows the status of your deployed VMs, including their region, health, and uptime.fly logs: Streams the real-time logs from your running application. This is invaluable for debugging.fly open: Opens your deployed application's URL in your default web browser.
This deployment process is also highlighted in another practical example.
Deploying an App to Fly.io | SvelteKit example app Dockerize and launch!
The video 'Deploying an App to Fly.io' by Consulting Ninja, while using SvelteKit, provides another excellent view of the core fly launch and deployment mechanics, reinforcing what we've learned.
Watch the sections covering fly launch (08:21 - 11:10) and verifying the deployment (11:10 - 12:04). Notice how the workflow is identical, regardless of the underlying web framework, because the deployment unit is the Docker container.
4. Continuous Deployment with GitHub Actions
Manually running fly deploy is great for getting started, but for a professional workflow, you'll want to automate deployments. Fly.io makes this easy.
When you first run fly launch, it may offer to create a GitHub Actions workflow file for you (.github/workflows/fly.yml). If you accept, it will create a file that automatically deploys your application whenever you push changes to your main branch.
This is also mentioned in the tanstack-bun-wasm-starter repository you might have seen.
tanstack-bun-wasm-starter Deployment Section
This GitHub repository for a Tanstack starter project includes a brief but important note on automating deployments.
Read the 'Deployment' section of the README. It highlights that flyctl launch can generate a GitHub Action and mentions the need to set a FLY_API_TOKEN as a repository secret for the action to work.
To make this work, you need to:
- Generate an API token:
fly auth token - Go to your GitHub repository's settings (
Settings>Secrets and variables>Actions). - Create a new repository secret named
FLY_API_TOKENand paste the token you generated.
Now, every push to your main branch will trigger the workflow, which will run fly deploy on your behalf.
Conclusion
In this lesson, we successfully bridged the gap from a local Docker image to a globally available, containerized application running on Fly.io.
Key Takeaways:
- Fly.io is a platform optimized for deploying Docker containers, making it a natural fit for the application we prepared in the previous lesson.
- The deployment workflow is centered around the
flyctlCLI, withfly launchfor initial setup andfly deployfor pushing new versions. - The
fly.tomlfile is the declarative configuration for your application on Fly.io, defining services, ports, and build instructions. - Secrets management is critical for production. Use
fly secrets setto securely provide environment variables to your application. - Automating deployments with GitHub Actions and a
FLY_API_TOKENestablishes a professional continuous deployment (CD) pipeline.
You now have a complete, end-to-end workflow for building, containerizing, and deploying a Tanstack Start application.
In our next lesson, we will address another critical aspect of production readiness: integrating a third-party error tracking service (e.g., Sentry) to capture both client and server-side errors. This will give us the visibility needed to maintain a healthy application in production.