You can deploy an Angular app to Hostinger shared hosting automatically on every push, with no FTP and no manual file copying — using Bitbucket Pipelines to build the app in CI and ship the compiled files over SSH. This guide walks through the whole pipeline: enabling SSH on Hostinger, wiring keys into Bitbucket, the bitbucket-pipelines.yml that builds and deploys, and the one .htaccess rule that stops your routes 404-ing. It targets a real shared plan (no VPS, no root, no Docker).
How the pipeline works
Shared hosting can’t run ng build — there’s no Node runtime and no root. So we split the job: Bitbucket builds, Hostinger serves. On every push to main, Bitbucket Pipelines spins up a Node container, runs npm ci and the production build, then uses rsync over SSH to copy only the changed compiled files into your Hostinger public_html. The result is a static deploy of your Angular bundle — fast, repeatable, and fully automated.
Prerequisites
- A Hostinger shared plan that includes SSH access (Premium and Business tiers do; confirm under hPanel before you start — see the verification note below).
- Your Angular app in a Bitbucket repository.
- An SSH key pair for the deploy. If you don’t have one, follow our guide on generating an SSH key.
Step 1 — Enable SSH and add your key on Hostinger
In hPanel, open Advanced → SSH Access (for a full walkthrough see how to SSH into Hostinger). Note three values you’ll need: the IP address, the username (looks like u123456789), and the port — Hostinger uses 65002, not 22. Turn SSH on if it isn’t already.
Still on that page, find Manage SSH keys → Import SSH Key and paste the public key (the .pub file) you’ll use for deployments — step-by-step in add SSH keys to Hostinger. Test the connection from your machine — remember Hostinger’s custom port:
ssh -p 65002 -i ~/.ssh/hostinger_ed25519 u123456789@your-server-ip
If juggling the port and key gets tedious, add a Host alias in ~/.ssh/config so you can just type ssh hostinger.
Step 2 — Give Bitbucket Pipelines the deploy key
Bitbucket needs the private key so the pipeline can log in to Hostinger. Don’t paste it into your YAML — use the built-in feature: Repository settings → Pipelines → SSH keys. You can generate a fresh key pair right there (then add its public half to Hostinger in Step 1) or paste an existing private key.
On the same screen, under Known hosts, enter your server and fetch its fingerprint so the pipeline trusts it — use the host with the custom port, e.g. your-server-ip:65002. Finally, store the connection details as repository variables (Repository settings → Repository variables) so they’re not hard-coded:
| Variable | Example value |
|---|---|
SSH_USER | u123456789 |
SSH_HOST | 123.45.67.89 |
REMOTE_PATH | /home/u123456789/public_html/ |
Step 3 — Add bitbucket-pipelines.yml
Commit this file to the root of your repo. It builds the app in one step, then deploys the compiled output in a second step using Atlassian’s rsync-deploy pipe, which automatically uses the SSH key you configured:
image: node:20
pipelines:
branches:
main:
- step:
name: Build Angular (production)
caches:
- node
script:
- npm ci
- npm run build -- --configuration production
artifacts:
- dist/**
- step:
name: Deploy to Hostinger
deployment: production
script:
- pipe: atlassian/rsync-deploy:1.3.0
variables:
USER: $SSH_USER
SERVER: $SSH_HOST
REMOTE_PATH: $REMOTE_PATH
LOCAL_PATH: 'dist/my-app/browser/*'
SSH_PORT: '65002'
DELETE_FLAG: 'true'
EXTRA_ARGS: '--exclude=.htaccess'
A few things worth calling out:
LOCAL_PATHmust match your build output. Angular 17+ emits todist/<app-name>/browser/; older versions usedist/<app-name>/. Check yourangular.jsonoutputPathand adjust.SSH_PORT: '65002'is the Hostinger-specific bit people forget — the deploy silently fails on port 22.DELETE_FLAG: 'true'removes stale files on the server so old bundles don’t linger, andEXTRA_ARGS: '--exclude=.htaccess'protects a hand-placed.htaccessfrom being wiped.- The
nodecache speeds up repeat builds by reusingnode_modules.
Step 4 — Fix Angular routing with .htaccess
Angular uses client-side routing, so refreshing a deep link like /dashboard makes Apache look for a file that doesn’t exist and return 404. Place this .htaccess in public_html (it’s excluded from the rsync above, so it survives deploys):
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^ index.html [L]
</IfModule>
We cover the edge cases (subfolder deploys, --base-href) in the dedicated guide on Angular and Laravel .htaccess fixes.
Step 5 — Push and watch it deploy
git add bitbucket-pipelines.yml
git commit -m "Add CI/CD deploy to Hostinger"
git push origin main
Open Pipelines in Bitbucket and watch the two steps run. When the deploy step goes green, load your domain — the Angular app is live, and every future push redeploys it automatically.
Troubleshooting
- “Permission denied (publickey)” in the deploy step. The public key isn’t on Hostinger, or Bitbucket is using a different key than you imported. Re-check Step 1 and the Pipelines SSH key.
- “Connection timed out” / “refused.” Wrong port — it must be
65002, set viaSSH_PORTand in the known-hosts entry. - Host key verification failed. You didn’t add the server (with its port) under Pipelines → Known hosts.
- Files deploy but the site is blank or 404s.
LOCAL_PATHpoints at the wrong folder — verify it matchesdist/<app>/browser/, and confirm the.htaccessfrom Step 4 is in place.
Deploying a Laravel backend the same way? See our companion guide on CI/CD with Bitbucket Pipelines for Laravel.
Frequently asked questions
Which SSH port does Hostinger use?
Hostinger shared hosting uses port 65002 for SSH, not the default 22. You’ll find your IP, username, and port under hPanel → Advanced → SSH Access.
Can I deploy Angular to Hostinger without a VPS?
Yes. An Angular production build is just static files, so any Hostinger shared plan with SSH can host it. You build in Bitbucket Pipelines and copy the compiled dist output to public_html over SSH — no VPS, root, or Docker required.
Why does my Angular app 404 after deploying?
Because Apache can’t find a file for client-side routes. Add an .htaccess in public_html that rewrites unknown paths to index.html so Angular’s router takes over.
