Deploying Python Projects and Scripts with GitHub Actions to DigitalOcean
In this guide, you'll learn how to set up automatic deployment for your Python app using GitHub Actions and DigitalOcean. This is a one-time setup: once it’s done, you can deploy updates just by pushing code to your repository with git push
.
Create a GitHub Repository
If you don’t already have a GitHub repository:
- Go to GitHub and click the + New repository button in the top-left corner.
- Choose your personal account or organization.
- Name your repository (e.g.
arkalos-app
). - Set it to Private.
- Skip other options and click Create repository.
- Push your local project to this repository:
# If you haven’t initialized Git commits yet:
git add .
git commit -m "init"
# Set main branch and push to GitHub
git branch -M main
git remote add origin git@github.com:your-username/your-repo.git # copy link from GitHub
git push -u origin main
Refresh your GitHub repository page — you should see your project files.
Set Up a DigitalOcean Droplet
- Sign up at DigitalOcean and create a Team and Project.
- Rename the project in the sidebar to something like
arkalos-app
. - Click Spin up a Droplet.
- Choose a region close to you.
- Under Choose an image, select Ubuntu 24.04 (LTS) x64.
- Choose the Basic (CPU Options: Regular) plan. Only $4/month. Or you may choose the cheapest Premium Intel option for $8/month with more power and space. If you plan to have a larger application and use AI, you will have to pick a larger option.
- Skip other options and under Authentication Method, select SSH Key and click Add SSH Key.
- If you don’t have an SSH key:
- Run
ssh-keygen
in your terminal (on VS Code/WSL). - Copy the contents of
~/.ssh/id_rsa.pub
into the box. - Name it something like "Your Laptop SSH Key".
- Run
- Check the Add improved metrics monitoring and alerting (free)
- Under the "Advanced Options" select Enable IPv6 (free).
- Set a hostname (e.g.
arkalos-app
or with your domain such asdashboard.arkalos.com
). - Keep quantity 1 and click Create Droplet. Once the droplet is ready, you’ll see a green dot indicating it’s active.
Add a Domain (Optional)
If you want to use a domain instead of the IP address:
Add a Subdomain
- Go to your domain provider and open DNS settings of your domain.
- Add a new A Record:
- Name: e.g.,
dashboard
(let say for arkalos.com domain, this will be dashboard.arkalos.com) - Value: your droplet’s IP address
- Name: e.g.,
- Add another A record:
- Name:
www.dashboard
- Value: same IP address
- Name:
Point a Full Domain to DigitalOcean
- Or instead, in DigitalOcean, go to Networking > Domains.
- Add your domain (e.g.,
arkalos.com
). - Create A records to point to the droplet:
@
→ droplet IPwww
→ droplet IP
- At your domain registrar, change nameservers to:
Note
If you change nameservers and had DNS settings previously, be sure to reconfigure any email or website or other settings in DigitalOcean.
Create a New User
Connect to Droplet via SSH
Approve the connection if prompted. Then update the system:
Choose to override config files with the maintainer's version if prompted.
Create a New User
adduser arkalos-app # specify a desired username, e.g. arkalos-app
usermod -aG sudo arkalos-app # add a user to sudo group
Switch to the new user:
Set Up SSH Access & GitHub Secrets
On Your Local Machine
# provide a comment in "", i.e. for what purpose is this key, and a file name at the end
ssh-keygen -t rsa -b 4096 -C "do__arkalos-app__your@email.com" -f ~/.ssh/do_arkalos_app_key
Copy the public key:
On Your Droplet
ssh root@your-droplet
su - arkalos-app
mkdir -p ~/.ssh && chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys
nano ~/.ssh/authorized_keys
# <Your name> & GitHub action's key for DigitalOcean
paste SSH public key here (right mouse click to paste)
Paste key into authorized_keys using nano editor. Save and exit with Ctrl+X
, Y
, Enter
.
Then update SSH config:
sudo nano /etc/ssh/sshd_config
# Ensure these lines are uncommented:
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
# Save with Ctrl+X, Y, Enter and restart the SSH service
sudo systemctl restart ssh
Add GitHub Repository Secrets
Go to Settings > Secrets and Variables > Actions and add:
DEPLOY_HOST
: your droplet IP or domainDEPLOY_USER
: arkalos-app or any Ubuntu username you specifiedDEPLOY_SSH_PRIVATE_KEY
: the content of your private key. Copy the output ofcat ~/.ssh/do_arkalos_app_key
DEPLOY_APP_DIR
: e.g.,/home/arkalos-app/apps/arkalos-app
Edit Local SSH Config
Host arkalos-app
HostName your-droplet-ip-or-domain
User arkalos-app
IdentityFile ~/.ssh/do_arkalos_app_key
Now test with:
If it works, disable root login.
Disable Root Login
sudo nano /etc/ssh/sshd_config
# Change to no:
PermitRootLogin no
# Save the file with Ctrl+X, Y, Enter and Restart the SSH
sudo systemctl restart ssh
Try logging in with root again — you should be denied.
Add a Deploy Key for the Server
On the Droplet, generate a key:
Copy the public key:
On GitHub, go to Settings > Deploy Keys, click Add Key, and paste it. Do not enable write access.
Update Server's SSH Config
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/github_deploy_key
IdentitiesOnly yes
Test the connection:
Set Up Project & Edit .env
Create an application directory and clone the repository.
mkdir -p ~/apps/arkalos-app
cd ~/apps/arkalos-app
# copy the link from GitHub, and make sure there is . at the end
git clone git@github.com:your-username/your-repo.git .
cp .env.example .env
uv sync
cd frontend
npm install
npm run build
cd ..
Update .env
with production credentials.
Install uv, NodeJS and PM2
Now, add final packages to the server.
Install uv:
Now you can check if uv is available by typing uv -V
.
Install NodeJS and npm:
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs
sudo npm install -g npm@latest
Test with node -v
; you shall see at least v22.14.0.
Lastly, install PM2:
PM2 is a simple, yet advanced, production process manager, that allows running scripts and apps on the background at all times and auto restart them.
sudo npm install pm2 -g
pm2 startup
# this will output the command,
# Copy and run it, it may look something like:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u arkalos-app --hp /home/arkalos-app
If you wish to test pm2 locally first on your computer and you are using a WSL, you may need to wrap the PATH= command in quotes, e.g. sudo env "PATH=$PATH:/usr/bin" ...
Test PM2 is running:
Check status with pm2 status
.
Check the details of the process with pm2 info arkalos-app
Check the logs with pm2 logs
or inside the new user's home direcotry ~/.pm2/logs.
If it looks good, save the process and enable it on startup, for example, when server is restarting, with pm2 save
.
Configure Nginx & SSL
Create a new website Nginx config file. If using a domain, Arkalos includes pre-configured files in .devops/nginx/sites-available/
that you could copy.
# sudo nano /etc/nginx/sites-available/<site name>, i.e.:
sudo nano /etc/nginx/sites-available/arkalos_app.conf
# or with a custom domain, specify your own domain
sudo nano /etc/nginx/sites-available/dashboard.arkalos.com.conf
Example config for IP-only:
If you have a domain, check the config example below.
server {
listen 80;
server_name YOUR_DROPLET_IP;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Then:
# Create a symbolic link to enable the website config
sudo ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/
# Disable the default site
sudo rm /etc/nginx/sites-enabled/default
# Test config
sudo nginx -t
# If it's good, reload the service
sudo service nginx reload
For SSL, HTTPS and HTTP2:
This will require a domain.
Generate a new SSL certificate for your domain. SPecify your domain with and without www:
Provide your email when asked.
Answer Y to the terms.
Answer Y or N if you wish to receive email updates.
Then you shall see "successfully deployed certificate" and your example.com nginx config file will be updated.
To verify auto-renewal is enabled:
Nginx site confing for SSL:
Note
If using domains and SSL, Arkalos projects come with the .devops
folder and a pre-configured site example file.
- Rename the
.devops/nginx/sites-available/example.com.conf
file to match the desired file name with your domain or app name. - Edit the file and replace
example.com
with your domain everywhere. In the VS Code, press Ctrl+F, search for example.com, then press the ">" arrow on the left and a new line will appear, type your domain into replace box, and click the last Replace All button to the right. - The file looks as such:
/etc/nginx/sites-available/example.com.conf (With SSL, HTTP2 & Redirects)
# /etc/nginx/sites-available/example.com.conf # Replace example.com everywhere with your domain # Redirect all HTTP traffic to HTTPS server { listen 80; listen [::]:80; server_name example.com www.example.com; return 301 https://example.com$request_uri; } # Redirect www to non-www over HTTPS server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name www.example.com; return 301 https://example.com$request_uri; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; } # Main server block for non-www server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
- After you updated the file in your project, make sure to git push, and then you can simply copy this file on your server. From your app directory:
- Make sure there is a symbolic link:
Final notes
Arkalos Python projects include pre-configured configs:
.github/workflows/deploy.yml
: Automatically runs GitHub deployment action on push to main..devops/nginx/...
: Ready-to-use Nginx configs.ecosystem.config.js
: PM2 config. Updatename
if needed, and reflect it indeploy.yml
.
Once this setup is complete, you can deploy by simply pushing to git.