Coolify Setup: Application Deployment with Self-Hosted PaaS
Coolify installation, Docker and Git integration, application deployment, database management, SSL certificates, environment variables, and self-hosted PaaS alternative to Heroku/Netlify.
Gitea or Forgejo Docker installation, repository management, CI/CD (Gitea Actions), SSH key configuration, organization and team management, mirror and webhook.
Gitea and Forgejo are open-source, lightweight, self-hosted alternatives to GitHub and GitLab. You can host Git repositories on your own server, run CI/CD pipelines, and collaborate on code with your team. Forgejo is a community-focused fork of Gitea, and both are largely compatible with each other.
| Feature | Gitea | Forgejo |
|---|---|---|
| License | MIT | GPL-3.0 |
| Governance | Company-backed | Community-governed |
| Compatibility | Reference implementation | Gitea-compatible |
| Updates | Regular | More frequent |
| Recommended for | Enterprise | Community |
Both projects use the same docker-compose structure; only the image name differs.
version: '3.8'
services:
gitea:
image: gitea/gitea:latest
container_name: gitea
restart: unless-stopped
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=db:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=strong-password
- GITEA__server__DOMAIN=git.example.com
- GITEA__server__SSH_DOMAIN=git.example.com
- GITEA__server__ROOT_URL=https://git.example.com/
- GITEA__server__SSH_PORT=2222
volumes:
- ./gitea-data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "127.0.0.1:3000:3000"
- "2222:22"
depends_on:
- db
db:
image: postgres:15
container_name: gitea-db
restart: unless-stopped
environment:
POSTGRES_DB: gitea
POSTGRES_USER: gitea
POSTGRES_PASSWORD: strong-password
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
postgres-data:
# Just change the image line:
image: codeberg.org/forgejo/forgejo:latest
# Start the container
docker compose up -d
# Check logs
docker logs gitea -f
server {
listen 80;
server_name git.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name git.example.com;
ssl_certificate /etc/letsencrypt/live/git.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/git.example.com/privkey.pem;
client_max_body_size 512M;
location / {
proxy_pass http://127.0.0.1:3000;
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 installation, go to https://git.example.com:
# /data/gitea/conf/app.ini
[server]
DOMAIN = git.example.com
ROOT_URL = https://git.example.com/
SSH_DOMAIN = git.example.com
SSH_PORT = 2222
DISABLE_SSH = false
[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = false
[mailer]
ENABLED = true
FROM = gitea@example.com
SMTP_ADDR = smtp.example.com
SMTP_PORT = 587
USER = gitea@example.com
PASSWD = smtp-password
# Generate SSH key
ssh-keygen -t ed25519 -C "user@example.com"
# Copy public key
cat ~/.ssh/id_ed25519.pub
In Gitea: Profile → Settings → SSH/GPG Keys → Add SSH Key
# Clone via SSH (port 2222)
git clone ssh://git@git.example.com:2222/user/repo.git
# Simplify with ~/.ssh/config
Host git.example.com
HostName git.example.com
User git
Port 2222
IdentityFile ~/.ssh/id_ed25519
# After config
git clone git@git.example.com:user/repo.git
# Create new repo and push
mkdir myproject && cd myproject
git init
git add .
git commit -m "Initial commit"
git remote add origin https://git.example.com/user/myproject.git
git push -u origin main
Creating a mirror from GitHub/GitLab:
New Repository → Migrate → GitHub/GitLab/Bitbucket
- Clone URL: https://github.com/user/repo.git
- Mirror (auto-sync): Enabled
- Sync interval: 8 hours
# Add to docker-compose.yml
runner:
image: gitea/act_runner:latest
container_name: gitea-runner
restart: unless-stopped
environment:
- GITEA_INSTANCE_URL=https://git.example.com
- GITEA_RUNNER_REGISTRATION_TOKEN=RUNNER_TOKEN
- GITEA_RUNNER_NAME=my-runner
volumes:
- ./runner-data:/data
- /var/run/docker.sock:/var/run/docker.sock
# .gitea/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
Create Organization:
+ → New Organization → Name and visibility
Create Team:
Organization → Teams → New Team
- Permissions: Owner, Admin, Write, Read
- Repository access: Specific or all
Invite Member:
Team → Members → Add User
Repository → Settings → Webhooks → Add Webhook
Payload URL: https://ci.example.com/webhook
Content Type: application/json
Secret: webhook-secret-key
Events: Push, Pull Request, Release
# Backup Gitea data
docker exec gitea gitea dump -c /data/gitea/conf/app.ini
# Database backup
docker exec gitea-db pg_dump -U gitea gitea > gitea-db-$(date +%Y%m%d).sql
# Full data directory
tar -czf gitea-backup-$(date +%Y%m%d).tar.gz ./gitea-data/
With Gitea/Forgejo on REXE VPS servers, you can manage your entire team's code infrastructure. 1 GB RAM is ideal for small to medium teams.
Both are technically almost identical. Forgejo is a community-governed fork of Gitea and receives more frequent updates. Gitea may be preferred for enterprise support or commercial use. Forgejo is recommended for those who prefer community-focused and free software. Both are compatible with the same clients and tools.
If SSH port is configured as 2222, you need to specify the port in the git clone command: ssh://git@git.example.com:2222/user/repo.git. You can add a Host entry to ~/.ssh/config to use the port automatically. Check that port 2222 is open in the server firewall.
Check that the runner container is running: docker ps | grep runner. Make sure the GITEA_RUNNER_REGISTRATION_TOKEN value is correct. Review runner logs: docker logs gitea-runner. Verify that the Gitea instance URL is accessible from the runner. Restart the container to re-register the runner.
Increase client_max_body_size in Nginx (e.g., 512M or 1G). Add MAX_CREATION_LIMIT to the [server] section in Gitea app.ini. Consider using Git LFS (Large File Storage): git lfs install && git lfs track '*.zip'. Check that LFS support is enabled in Gitea.
Use the Migrate feature in Gitea: New Repository → Migrate → GitHub. You can also migrate private repositories with a GitHub Personal Access Token. With the Mirror option, changes on GitHub are automatically synchronized. All branches, tags, and commit history are preserved.
Take a backup before updating: docker exec gitea gitea dump. Then update with docker compose pull && docker compose up -d. Gitea performs automatic database migrations. For large version jumps (e.g., 1.19 → 1.21), you may need to go through intermediate versions one by one. Always read the release notes.
Coolify installation, Docker and Git integration, application deployment, database management, SSL certificates, environment variables, and self-hosted PaaS alternative to Heroku/Netlify.
Nextcloud installation with Docker, Apache/Nginx configuration, database setup, SSL certificate, file synchronization, app store, and performance optimization.
Portainer CE installation, Docker and Kubernetes management, stack deployment, volume/network management, user authorization, webhook and GitOps integration.