Deploy Apps with Nixpacks on a VPS
Turn your source code into production-ready containers without writing a single Dockerfile. Supports Node.js, Python, Go, Rust, Ruby, PHP, Java, and 20+ more languages.
At a Glance
| Project | Nixpacks by Railway |
| License | MIT |
| Recommended Plan | RamNode Cloud VPS 2GB or higher |
| OS | Ubuntu 22.04 / 24.04 LTS |
| Languages | Node.js, Python, Go, Rust, Ruby, PHP, Java, .NET, and 20+ more |
| Status | Maintenance mode (Railpack is successor); fully functional and widely used |
| Estimated Setup Time | 15–20 minutes |
Prerequisites
- A RamNode VPS with at least 1 GB RAM (2 GB recommended for larger builds)
- Ubuntu 22.04 or 24.04 LTS
- Root or sudo access to the server
- A domain name (optional but recommended for HTTPS)
- Basic familiarity with the Linux command line
Initial Server Setup
sudo apt update && sudo apt upgrade -ysudo apt install -y curl git wget ca-certificates gnupg lsb-releaseInstall Docker
Nixpacks uses Docker BuildKit to produce container images. Docker must be installed first.
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginsudo systemctl enable docker
sudo systemctl start docker
docker --versionsudo usermod -aG docker $USER
newgrp dockerInstall Nixpacks
curl -sSL https://nixpacks.com/install.sh | bashnixpacks --versionAlternatively, install a specific version from GitHub releases:
curl -LO https://github.com/railwayapp/nixpacks/releases/download/v1.40.0/nixpacks-v1.40.0-amd64.deb
sudo dpkg -i nixpacks-v1.40.0-amd64.debBuild Your First Application
Create a Sample Node.js App
mkdir ~/sample-app && cd ~/sample-app{
"name": "nixpacks-demo",
"version": "1.0.0",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.18.0"
}
}const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.json({
message: 'Hello from Nixpacks on RamNode!',
timestamp: new Date().toISOString()
});
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`Server running on port ${PORT}`);
});Generate a Build Plan
nixpacks plan .This outputs a JSON object showing the detected language, packages, install command, and start command.
Build and Run
nixpacks build . --name sample-appdocker run -d --name sample-app -p 3000:3000 -e PORT=3000 sample-appcurl http://localhost:3000Understand the Build Process
Plan phase: Nixpacks analyzes the source directory, detects the language from marker files (package.json, requirements.txt, go.mod, Cargo.toml), and generates a build plan.
Build phase: Nixpacks converts the plan into a Dockerfile, then uses Docker BuildKit to produce the final OCI-compliant container image.
Supported languages: Node.js, Python, Go, Rust, Ruby, PHP, Java, .NET/C#, Elixir, Deno, Dart, Haskell, Crystal, Clojure, Scala, Swift, Zig, and static HTML sites.
Customize Builds with nixpacks.toml
[phases.setup]
nixPkgs = ['nodejs', 'yarn']
aptPkgs = ['libpq-dev']
[phases.install]
cmds = ['yarn install --frozen-lockfile']
[phases.build]
cmds = ['yarn run build']
[start]
cmd = 'yarn run start'Adding System Dependencies
[phases.setup]
nixPkgs = ['...', 'ffmpeg'] # '...' preserves auto-detected packages
aptPkgs = ['libmagickwand-dev']Specifying Language Versions
[variables]
NIXPACKS_NODE_VERSION = '20'
NIXPACKS_PYTHON_VERSION = '3.12'Or at the command line:
nixpacks build . --name my-app --env NIXPACKS_NODE_VERSION=20Custom Build Phases
[phases.migrate]
cmds = ['npx prisma migrate deploy']
dependsOn = ['build']
[start]
cmd = 'node dist/server.js'Python Application Example
mkdir ~/python-app && cd ~/python-appflask==3.0.0
gunicorn==21.2.0from flask import Flask, jsonify
import os
app = Flask(__name__)
@app.route('/')
def hello():
return jsonify({
'message': 'Python app built with Nixpacks on RamNode',
'python': 'auto-detected'
})
if __name__ == '__main__':
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)web: gunicorn app:app --bind 0.0.0.0:$PORTnixpacks build . --name python-app
docker run -d --name python-app -p 5000:5000 -e PORT=5000 python-appGo Application Example
mkdir ~/go-app && cd ~/go-app
go mod init go-apppackage main
import (
"fmt"
"net/http"
"os"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{"message": "Go app on RamNode via Nixpacks"}`)
})
fmt.Printf("Listening on port %s\n", port)
http.ListenAndServe("0.0.0.0:"+port, nil)
}nixpacks build . --name go-app
docker run -d --name go-app -p 8080:8080 -e PORT=8080 go-appNginx Reverse Proxy with SSL
sudo apt install -y nginx certbot python3-certbot-nginxserver {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
proxy_cache_bypass $http_upgrade;
}
}sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxsudo certbot --nginx -d yourdomain.comContainer Management and Updates
Rebuild After Code Changes
cd ~/sample-app
git pull
nixpacks build . --name sample-app
docker stop sample-app
docker rm sample-app
docker run -d --name sample-app -p 3000:3000 -e PORT=3000 sample-appClean Up Old Images
docker image prune -f
docker image prune -a -fLogs and Auto-Restart
docker logs sample-app
docker logs -f sample-appdocker run -d --name sample-app --restart unless-stopped -p 3000:3000 -e PORT=3000 sample-appEnvironment Variables and Secrets
Build-Time Variables
nixpacks build . --name my-app \
--env NODE_ENV=production \
--env DATABASE_URL=postgresql://user:pass@db:5432/mydbRuntime Variables
docker run -d --name my-app \
-p 3000:3000 \
-e PORT=3000 \
-e DATABASE_URL=postgresql://user:pass@db:5432/mydb \
-e REDIS_URL=redis://redis:6379 \
-e SECRET_KEY=your-secret-key-here \
my-appdocker run -d --name my-app \
-p 3000:3000 \
--env-file .env.production \
my-appDocker Compose for Multi-Container Deployments
version: '3.8'
services:
app:
image: sample-app
container_name: sample-app
restart: unless-stopped
ports:
- "3000:3000"
environment:
- PORT=3000
- DATABASE_URL=postgresql://appuser:apppass@db:5432/appdb
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
db:
image: postgres:16-alpine
container_name: app-db
restart: unless-stopped
environment:
- POSTGRES_USER=appuser
- POSTGRES_PASSWORD=apppass
- POSTGRES_DB=appdb
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7-alpine
container_name: app-redis
restart: unless-stopped
volumes:
pgdata:nixpacks build . --name sample-app
docker compose up -dBuild Optimization Tips
Enable BuildKit Caching
export DOCKER_BUILDKIT=1Pin Nixpkgs for Reproducible Builds
nixpacks build . --name my-app --pinReduce Image Size
Separate dependencies from devDependencies in package.json. Keep requirements.txt lean. Use a custom start command:
[start]
cmd = 'node dist/index.js'Troubleshooting
- "No provider found" — Ensure the correct marker file exists (
package.json,requirements.txt,go.mod, etc.). - Out of memory during builds — Upgrade to a 2 GB plan or add swap space.
- Docker permission denied — Add your user to the
dockergroup and re-login. - Port conflicts — Check with
sudo lsof -i :3000. - Slow builds — First-time builds are slow; subsequent builds use layer caching.
