Deploying your Astro blog
Keep your Astro source private and publish only the static build
GitHub Pages for private respositories is not supported on GitHub Free (read here) and I didn’t like the idea of making the whole blog’s source code and post drafts publicly available. So I had to come up with a solution.
NOTE
I’m aware that Cloudfare Pages it’s free, faster than GitHub Pages, and supports private repos. But I also had the comments section tied to the public repo so. Maybe next time…
The idea is simply using a two-repository strategy:
- Private repo - Where we keep our source code with drafts, work-in-progress posts, and all our Astro source files.
- Public repo - Just the contents of the
dist/folder, for GitHub Pages deployment.
Configuring the Deployment Target
By deployment target, I mean the public repo from where we will will serve the site to the world, using GitHub Pages.
NOTE
Remember, our private repo is where we write posts, keep drafts, and build the Astro project. The public repo is just where we push the final static output.
In our public repo, we have to configure a publishing source for GitHub Pages. There are a couple of options here:
- To publish when changes are pushed to a specific branch.
- Or you can write a GitHub Actions workflow to publish your site.
We’ll be using the first option; just click on the Settings tab of your repo, and once there, find the Pages slot in the left sidebar. Mine looked like this:

What this means is that whenever we push changes to the master branch (root folder /), our GitHub pages will be publish.
For Astro Users
Before building our site, we need to tell Astro the final public URL where our site will be published. This is controlled by two settings in astro.config.ts:
- site is the domain where we will serve the site.
- base is the path under that domain where your site will live.
Since we will be serving the page from the URL of the public repo, we need to add it to our astro.config.ts:
export default defineConfig({ site: 'https://<username>.github.io', base: '/', // more stuff...})If your public repo is the special GitHub Pages repo named <username>.github.io, then the site is served from the root:
https://<username>.github.io/.
TIP
In my case, since it was my personal page, I used the https://<username>.github.io URL. But it doesn’t have to be, any public repo URL will do, e.g. https://github.com/<username>/<repo-name>.
But if you’re publishing to a repo named foo, then you should use something like:
site: 'https://github.com'base: 'foo'
TLDR
Just three steps:
- Build your project; from the private repo run:
npm run build- Copy the contents of the
distfolder, from the private repo to the public repo:
rsync -a --delete --exclude '.git' dist/ ../my-blog-public/- Move to the public repo, and push changes:
cd ../my-blog-publicgit add -Agit commit -m 'new build'git push1. Creating the Build
Not much to say here, whenever you add a new post, and are ready to publish, you need to build right? Let’s assume you have the following build script in the package.json of your private repo:
"build": "astro build"We just have to change to the root of your private repo, and run:
npm run buildIn the case of an Astro project, we should end up with a dist folder containing the artifacts of our build.
2. Copy the Build to the Public Repo
Now the goal is to move the contents of the dist folder (not the folder itself, but its contents), to the root of our public repo. :
rsync -a --delete --exclude '.git' dist/ ../my-blog-public/Let’s go over the flags:
dist/with trailing slash copies the contents of dist, not the folder itself.--deleteremoves stale files from the public repo.--exclude '.git'preserves the public repo Git metadata.- It copies hidden files like
.nojekyll.
TIP
In Astro, dist is the default build folder; but if you’re using another one, or not even using Astro, feel free to use whatever folder you need to.
The command above assumes that you have cloned your public repo to the parent folder of the private repo. Check the following folder structure:
parent-folder/ ├── my-private-repo/ # 👈 We're working here. │ ├── src/ │ ├── content/ │ └── dist/ └── my-blog-public/ # 👈 Make sure you have cloned your public repo. └── .git/3. Pushing Changes to the Public Repo
Push the changes to your GitHub public remote:
cd ../my-blog-publicgit add -A # Stage the changesgit commit -m "New build" # Commitgit push # PushThat should be enough you have your page live after a few seconds.