By default Astro is building our site as statically generated (SSG). Because we want to add some runtime features to our blog and interface with a database in the future we need to convert it to be server-side rendered (SSR).
Astro has integrations for this, because we know we want to deploy to Fly.io via docker we'll use the Node.js integration and set the output to 'hybrid'. Hybrid, as opposed to 'server', will still statically generate the pages of our blog that are static but give us the option of SSR when we need it.
In this lesson we'll refactor our build to using docker and deploy to Fly.io
Lazar Nikolov: [0:00] In the previous lesson, we set up our Fly.io account and ran the fly launch command which generated the files we needed for deployment. In this lesson, we're going to modify the Dockerfile for our project. First, let's talk about Astro deployments.
[0:15] Astro supports all cloud platforms, but it has first-class support for some of them like Vercel, Netlify, GitHub pages, AWS, etc. Our website right now is configured as a static website. We could serve it as it is, but since we're going to build an API, we need to convert it to SSR.
[0:35] Astro has different SSR adapters for different platforms and frameworks. Since we're going to deploy it on a Node.js server, we should add the Node SSR adapter. To do that, we're going to run npx astro add node. This will install the astro.js/node package and update the Astro config file. Let's proceed.
[0:56] Now we can open the Astro config file. We can see that the output property is set to server. By the way, this is all below the integrations array. Our output is being set to server and the adapter property is set to node, where the mode is standalone. This current configuration makes all of our pages server-side rendered. We lose the static speeds this way.
[1:21] Because we still want our pages to be statically served, let's change the output to hybrid. Hybrid will make our server serve files statically by default unless we specify that we want server-side rendering. We're going to learn how to do that in a lesson later on. For now, we just want to have the output set to hybrid so that the Node.js server still serves the files statically.
[1:46] If we run npm run build now, we're going to see a slightly different output. Inside our disk directory, we can see that there is a client and a server directory. The server directory has an entry.mjs file. That's our Node.js server. Keep this file in mind and let's proceed to the Dockerfile.
[2:07] The initial Dockerfile won't work for our project because it contains some commands that we don't have in our project. Let's just delete all of it and start from scratch. The first line, we're going to define the base image we're going to use, which is going to be node:lts-alpine.
[2:26] We're going to alias it as runtime. This tells Docker to use a Linux distribution optimized and equipped to run Node.js apps. Then we're going to set the work directory to /app. This means that all of the commands are going to be executed in the /app directory within the Docker image. Then we're going to copy our whole project to the image, so copy..
[2:52] Now we got our code inside of the image, so let's set it up. You can think of this process as cloning the repo to your machine and setting it up to run. The first thing you do is run the npm install command to install all of the dependencies. Through this line, we're actually installing the dependencies within the Docker image.
[3:13] After installing the dependencies, you would like to run npm run build to build the actual project. At this point, our Astro project is ready to be started, but before we do that, we need to define some environment variables. We can do that by using the env command.
[3:29] Let's define the host variable and set it to 0000. We can define the port variable to 4321, which is the default Astro port. We're going to define the mode to production. These are all the variables that we need. Since we defined the port to 4321, we need to expose this port to outside of the Docker image. we can do that by using the expose command and providing the port value.
[3:57] Up until this point, all of the commands that we wrote are going to be executed during the image build time. The last command is going to be executed when we deploy our app and fly.io starts our apps container. Instead of using the run command, we need to use the cmd command. Then we can do npm run start.
[4:21] With this, we've built our apps image and instructed the container how to run the website. There's one last thing to do, and that is to modify the start npm command to actually run our node.js server. Let's open the package.json file.
[4:38] Remember that file from before, the entry.mjs? We need to run that file with node, so let's replace the current start command with node./dist/server/entry.mjs.
[4:52] We can run this locally, npm run start, just to see that everything works. You can see that the astro.js node package logged out, server listening on HTTP 127...1 on port 4321. If we open that in our browser, we're going to see our website, so everything is working properly.
[5:14] Now we're ready to run deployment. Before we do, make sure to check out the fly.toml file. The app name should match the one from your dashboard. Also, the internal port of the HTTP servers should be 4321. In case it's something different in your case, just change it to 4321. Now we're ready.
[5:35] Now if we run fly deploy, the CLI application is going to load our fly.toml file, and then it's going to build the Docker image. As you can see, it executes all the commands that we defined in the Dockerfile. After it builds the image, it's going to deploy it under our account.
[5:53] If you decided not to add a credit card info, your first fly deploy command might end with an error saying that you need to provide billing information in order to have more than one active machine.
[6:06] A weird fix for this is to just simply run the command again. When the CLI application deploys your command is going to let you know that the update has successfully finished. It has finished deploying, and that your newly deployed application is available at this URL.
[6:24] If we open this one in our browser, there we go, we're going to see our blog. Congratulations. Your website is now deployed and it's live. All right, let's do a recap now.
[6:34] Astro is static by default. Since we're going to be building our own API that connects to our database to retrieve data, we needed to change Astro's behavior to SSR mode, which we did by installing a specific SSR adapter. In our case, we're deploying our website with Docker on a Node.js server, so we needed to use the node adapter.
[6:56] To do that, we ran npx astro add node. Since this changed Astro's behavior and therefore its output, we needed to change the start script to run our Node.js server instead of running Astro dev. Then we proceeded to create our Dockerfile.
[7:13] We picked the node lts-Alpine as our base image, copied our whole project, installed the dependencies, built the website, defined a few environment variables for our Node.js server, exposed the 4321 port, and at the end, ran the start npm command.
[7:31] With our Dockerfile ready and our astro project converted to SSR, we were ready to deploy our website. Running the fly deploy command, built our Docker image, and deployed it to the fly servers. Additionally, it assigned a fly domain to it.