Derafu Deployer is a lightweight deployment system for managing multiple Django sites using Fabric2 . It supports deployment via local, SSH, or Docker runners, and provides key features like automatic backups, shared files/directories management, and per-site configuration.
- Multi-Runner Support: Automatically runs tasks locally, via SSH, or inside Docker containers.
- Logging: All deployment logs are streamed to console.
- Backups: Before each deploy, a
.tar.gzbackup is created and stored with support for limiting how many are kept. - Shared Files & Dirs: Automatically symlinks files and folders like
.env,media/, etc. - Writable Dirs: Configure folders that require specific permissions (e.g., 775).
- Dynamic Site Detection: Uses
sites.ymlto configure and deploy any number of Django projects. - Versioned Releases: Each deploy is stored in
releases/YYYYMMDD_HHMMSS_mmm, keeping history clean and organized. - Symlink-Based Switching: The
currentsymlink always points to the latest working release, enabling atomic deployments. - Rollback on Failure: If a deployment fails, the previous version is automatically restored.
- Manual Rollback: You can manually trigger a rollback with
fab2 rollback --site=.... - Old Releases Cleanup: Keeps only the last
Nreleases (default: 5) to avoid clutter. - Release Locking: Prevents concurrent deployments using a
.deploy.lockfile. - Manual Unlock: Use
fab2 unlock --site=...to remove a lock after an interrupted deployment.
- Python 3.13+
- Fabric2 3.2
- SSH access to your servers.
- Docker (if using Docker runner)
- Git repositories for your projects.
pip install -r requirements.txtNote: The tool is designed to be used standalone, not inside other project.
.
├── LICENSE # License file
├── README.md # Project documentation
├── fabfile.py # Entry point for Fabric tasks (e.g., deploy, rollback)
├── pyproject.toml # Optional project metadata and build configuration
├── requirements.txt # Python dependencies (e.g., fabric2, invoke)
├── siteadd.py # Script to add new site entries to `sites.yml`
├── sites-dist.yml # Example configuration file (template)
├── sites.yml # Main configuration file with all project deployments
├── fabricator/ # Core deployment logic and utilities
│ ├── __init__.py # Marks fabricator as a Python package
│ ├── deploy.py # Main orchestrator for deploying sites
│ ├── logger.py # Logging utilities (console output + formatting)
│ ├── recipes.py # Reusable deployment steps (migrate, restart, etc.)
│ ├── runners.py # Abstracts local, SSH, and Docker command execution
│ ├── utils.py # Utility functions (load_sites, print_site_list)
│ └── exceptions/ # Custom exception handling (e.g., deploy failures)
│ ├── __init__.py # Package initializer for exceptions
│ └── deployer_exceptions.py # Custom exception classes for deployment errors
The main configuration is stored in sites.yml, based on sites-dist.yml. Each key represents a site.
Edit the sites.yml file located in the fabricator/ directory to configure your websites.
Each key represents a domain name (e.g., app.example.com). The value can be:
- A dictionary with detailed configuration options.
Example:
app.example.com:
repository: git@github.com:example/example.git
branch: master
deploy_path: /var/www/sites/app.example.com
venv: .venv
runner: docker
docker_container: my-container
docker_user: admin
backup_path: /var/www/sites/backuplocal
max_backups: 5
shared_files:
- .env
shared_dirs:
- media
writable_dirs:
- mediaFor simple configurations you can use:
./siteadd.py app.example.com git@github.com:example/example.git| Key | Required | Default | Description |
|---|---|---|---|
repository |
Yes | — | Git repository to clone |
branch |
No | master |
Branch to deploy |
deploy_path |
Yes | — | Path where the site will be deployed |
venv |
No | .venv |
Virtualenv directory name |
runner |
No | local |
Can be local, docker, or ssh |
docker_container |
Cond. | — | Required if runner is docker |
docker_user |
No | — | User to run Docker commands as |
backup_path |
Yes | — | Where to store backups (must be defined) |
max_backups |
No | 5 |
How many backups to keep |
shared_files |
No | [] |
List of files to symlink after each deploy |
shared_dirs |
No | [] |
List of directories to symlink after each deploy |
writable_dirs |
No | [] |
Directories to make writable (chmod 775, etc.) |
To see all configured sites:
fab2 list-sitesDefault runner is local, so this will deploy locally:
fab2 deploy --site=app.example.comSpecify the site which has "runner: docker" in sites.yml:
fab2 deploy --site=app.example.comYou can also define the Docker container and user in the site config:
app.example.com:
runner: docker
docker_container: my-container
docker_user: adminWhen "runner: ssh" is configured in sites.yml, the deploy will be done over SSH:
app.example.com:
runner: ssh
host: 192.168.1.99
user: deployerYou can override the SSH connection with environment variables:
DEPLOYER_HOST=192.168.1.99 fab2 deploy --site=app.example.comOr also set user and port:
DEPLOYER_HOST=192.168.1.99 DEPLOYER_USER=admin DEPLOYER_PORT=22 fab2 deploy --site=app.example.comDeploy all sites defined in sites.yml, one by one:
fab2 deploy-allYou can also deploy all sites remotely by using environment variables:
DEPLOYER_HOST=192.168.1.99 DEPLOYER_USER=admin fab2 deploy-allOr limit to a specific runner (e.g., local only):
fab2 deploy-all --runner=localIf a deployment fails during a critical step (like migrations), the system will automatically rollback to the previous release. This prevents broken sites from going live.
You can manually revert to the last successful release at any time using:
fab2 rollback --site=app.example.comThis will:
- Restore the last valid release from
releases/ - Update the
currentsymlink - Restart Gunicorn or the appropriate service
fab2 rollback-allIf a deployment was interrupted and the .deploy.lock file wasn't removed:
fab2 unlock --site=app.example.comThis forcibly removes the lock file and allows future deploys.
Force unlock for all sites at once (use with caution):
fab2 unlock-allFor each site, the deployment process performs the following steps:
- Acquire Lock: Prevents concurrent deployments by creating a
.deploy.lockfile. If the site is already locked, the deploy is aborted. - Check Remote: Verifies SSH connection and deployment path.
- Create Backup: Compresses current state before deploying.
- Update Code: Clones Git repository into a temporary folder.
- Versioned Release Folder: Moves code into
releases/YYYYMMDD_HHMMSS_mmm/and updates thecurrentsymlink. - Cleanup Old Releases: Keeps only the latest
Nreleases (configurable). - Shared Files/Dirs: Links shared files and directories (e.g.,
.env,media/). - Writable Dirs: Applies proper permissions to specified folders.
- Install Deps: Installs Python dependencies in a virtualenv.
- Validate Migrations: Runs
python manage.py migrate --planto ensure migrations won't fail. - Migrate: Applies Django database migrations.
- Collect Static: Gathers static assets with
collectstatic. - Restart Services: Reloads services like Gunicorn via socket or process restart.
- Rollback on Failure: If a critical step fails, the system rolls back to the previous working release.
- Release Lock: Always removes the
.deploy.lockfile at the end of the process. - Logging: Logs are streamed to console.
You can customize the following behaviors in recipes.py:
- How backups are made.
- How services are restarted.
- How static files and dependencies are handled.
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
This package is open-sourced software licensed under the MIT license.