📖 Looking for karrio's legacy docs? Visit docs.karrio.io

Custom Builds

This guide provides developers with advanced techniques to customize Karrio through custom Docker builds, extension plugins, and additional Python packages.

Why Customize Karrio?

There are several reasons you might want to customize your Karrio deployment:

  • Custom carriers: Add support for carriers not included in the standard distribution
  • Custom integrations: Connect to your internal systems or third-party platforms
  • Enhanced functionality: Add custom business logic or unique features
  • Performance optimizations: Tune the system for your specific workloads
  • Compliance requirements: Meet specific regulatory or security requirements

Prerequisites

  • Familiarity with Docker and Docker Compose
  • Basic understanding of Python and Django development
  • Git installed on your system
  • Docker and Docker Compose installed
  • Understanding of Karrio’s architecture

Creating Custom Docker Images

Basic Approach: Extending the Official Image

The simplest way to customize Karrio is to extend the official Docker image with your own modifications.

  1. Create a directory for your custom build:
1mkdir -p ~/karrio-custom 2cd ~/karrio-custom
  1. Create a Dockerfile that extends the official image:
Dockerfile
1FROM karrio/server:latest 2 3# Add your custom dependencies 4RUN pip install your-custom-package another-package 5 6# Add your custom files 7COPY ./custom_files /custom_files 8 9# Set environment variables if needed 10ENV CUSTOM_SETTING=value
  1. Create a docker-compose.yml file that uses your custom image:
docker-compose.yml
1version: '3' 2 3services: 4 api: 5 build: . 6 container_name: karrio.api 7 restart: unless-stopped 8 ports: 9 - ${KARRIO_HTTP_PORT:-5002}:${KARRIO_HTTP_PORT:-5002} 10 depends_on: 11 - db 12 - redis 13 environment: 14 DEBUG_MODE: ${DEBUG_MODE:-False} 15 DETACHED_WORKER: "True" 16 SECRET_KEY: ${SECRET_KEY} 17 REDIS_HOST: ${REDIS_HOST:-redis} 18 REDIS_PORT: ${REDIS_PORT:-6379} 19 DATABASE_HOST: ${DATABASE_HOST:-db} 20 DATABASE_PORT: ${DATABASE_PORT:-5432} 21 DATABASE_NAME: ${DATABASE_NAME:-db} 22 DATABASE_ENGINE: ${DATABASE_ENGINE:-postgresql_psycopg2} 23 DATABASE_USERNAME: ${DATABASE_USERNAME:-postgres} 24 DATABASE_PASSWORD: ${DATABASE_PASSWORD:-postgres} 25 KARRIO_HTTP_PORT: ${KARRIO_HTTP_PORT:-5002} 26 # Add your custom environment variables here 27 CUSTOM_SETTING: ${CUSTOM_SETTING:-value} 28 volumes: 29 - karrio-static:/karrio/static 30 - ./plugins:/karrio/plugins # Mount plugins directory 31 32 worker: 33 build: . 34 container_name: karrio.worker 35 restart: unless-stopped 36 depends_on: 37 - db 38 - redis 39 - api 40 environment: 41 DEBUG_MODE: ${DEBUG_MODE:-False} 42 REDIS_HOST: ${REDIS_HOST:-redis} 43 REDIS_PORT: ${REDIS_PORT:-6379} 44 DATABASE_HOST: ${DATABASE_HOST:-db} 45 DATABASE_PORT: ${DATABASE_PORT:-5432} 46 DATABASE_NAME: ${DATABASE_NAME:-db} 47 DATABASE_ENGINE: ${DATABASE_ENGINE:-postgresql_psycopg2} 48 DATABASE_USERNAME: ${DATABASE_USERNAME:-postgres} 49 DATABASE_PASSWORD: ${DATABASE_PASSWORD:-postgres} 50 # Add your custom environment variables here 51 CUSTOM_SETTING: ${CUSTOM_SETTING:-value} 52 volumes: 53 - ./plugins:/karrio/plugins # Mount plugins directory 54 command: "/bin/bash ./worker" 55 56 # Include other services from the standard docker-compose.yml 57 dashboard: 58 image: karrio/dashboard:${KARRIO_TAG:-latest} 59 # ... rest of dashboard configuration 60 61 db: 62 # ... database configuration 63 64 redis: 65 # ... redis configuration 66 67volumes: 68 karrio-static: 69 postgres-data: 70 redis-data:
  1. Create a .env file with your configuration:
.env
1SECRET_KEY=your_secure_secret_key 2DEBUG_MODE=False 3KARRIO_HTTP_PORT=5002 4KARRIO_TAG=latest 5 6# Database settings 7DATABASE_ENGINE=postgresql_psycopg2 8DATABASE_NAME=db 9DATABASE_USERNAME=postgres 10DATABASE_PASSWORD=postgres 11DATABASE_HOST=db 12DATABASE_PORT=5432 13 14# Redis settings 15REDIS_HOST=redis 16REDIS_PORT=6379 17 18# Your custom settings 19CUSTOM_SETTING=value
  1. Build and start your custom Karrio deployment:
1docker-compose build 2docker-compose up -d

Advanced Approach: Building from Source

For more extensive customizations, you can build Karrio from source:

  1. Clone the Karrio repository:
1git clone https://github.com/karrioapi/karrio.git 2cd karrio
  1. Create a custom Dockerfile in the repository:
Dockerfile.custom
1FROM python:3.11-slim AS compile-image 2 3ARG REQUIREMENTS="requirements.txt" 4ARG CUSTOM_REQUIREMENTS="custom-requirements.txt" 5 6RUN apt-get update -y && apt-get install -y gcc 7 8RUN python -m venv /karrio/venv 9ENV PATH="/karrio/venv/bin:$PATH" 10 11# Install standard requirements 12COPY "${REQUIREMENTS}" /temp/ 13RUN pip install --upgrade pip && \ 14 pip install -r "/temp/${REQUIREMENTS}" 15 16# Install custom requirements if present 17COPY "${CUSTOM_REQUIREMENTS}" /temp/ || true 18RUN if [ -f "/temp/${CUSTOM_REQUIREMENTS}" ]; then \ 19 pip install -r "/temp/${CUSTOM_REQUIREMENTS}"; \ 20 fi 21 22# The runtime image 23FROM python:3.11-slim AS build-image 24 25LABEL maintainer=your-email@example.com 26LABEL org.opencontainers.image.title="custom karrio server" 27LABEL org.opencontainers.image.description="custom karrio server runtime image" 28 29ENV DEBUG_MODE=False 30ENV USE_HTTPS=False 31ENV ALLOWED_HOSTS=* 32ENV KARRIO_WORKERS=2 33ENV BACKGROUND_WORKERS=2 34ENV DETACHED_WORKER=False 35ENV WORK_DIR=/karrio/app 36ENV LOG_DIR=/karrio/log 37ENV WORKER_DB_DIR=/karrio/data 38ENV STATIC_ROOT_DIR=/karrio/static 39ENV PLUGINS_DIR=/karrio/plugins 40 41RUN apt-get update -y && apt-get install -y libpango1.0-0 libpangoft2-1.0-0 gcc zint curl 42RUN useradd -m karrio -d /karrio 43USER karrio 44 45COPY --chown=karrio:karrio --from=compile-image /karrio/ /karrio/ 46RUN mkdir -p $WORK_DIR $LOG_DIR $WORKER_DB_DIR $STATIC_ROOT_DIR $PLUGINS_DIR 47COPY apps/api/gunicorn-cfg.py docker/api/entrypoint docker/api/worker $WORK_DIR/ 48 49# Copy your custom code 50COPY custom/ /karrio/custom/ 51 52WORKDIR $WORK_DIR 53 54# Make sure we use the virtualenv 55ENV PATH="/karrio/venv/bin:$PATH" 56 57# Add PYTHONPATH for plugins 58ENV PYTHONPATH="${PYTHONPATH}:/karrio/plugins" 59 60ENTRYPOINT ["dumb-init", "--"] 61CMD ["./entrypoint"]
  1. Create a custom-requirements.txt file with your additional Python dependencies:
custom-requirements.txt
1your-custom-package==1.0.0 2another-package==2.3.4
  1. Create a custom docker-compose.yml file:
docker-compose.custom.yml
1version: '3' 2 3services: 4 api: 5 build: 6 context: . 7 dockerfile: Dockerfile.custom 8 args: 9 - REQUIREMENTS=requirements.txt 10 - CUSTOM_REQUIREMENTS=custom-requirements.txt 11 # ... rest of configuration similar to above
  1. Build and run your custom deployment:
1docker-compose -f docker-compose.custom.yml build 2docker-compose -f docker-compose.custom.yml up -d

Setting Up Mountable Plugin Directories

Karrio can be extended with plugins loaded from a mounted directory, allowing you to update and add functionality without rebuilding the Docker image.

Creating the Plugin Structure

  1. Create a plugins directory in your project:
1mkdir -p plugins
  1. A Karrio plugin follows this basic structure:
1plugins/ 2├── my_plugin/ 3│ ├── __init__.py 4│ ├── carrier.py # For custom carrier integrations 5│ ├── models.py # For custom data models 6│ ├── services.py # For custom business logic 7│ └── hooks.py # For event hooks 8└── another_plugin/ 9 └── ...
  1. The __init__.py file should register your plugin:
plugins/my_plugin/__init__.py
1from karrio.core.plugins import Plugin 2 3class MyPlugin(Plugin): 4 """Custom plugin for extending Karrio.""" 5 6 id = 'my_plugin' 7 name = 'My Custom Plugin' 8 version = '1.0.0' 9 10 def initialize(self): 11 # This method is called when the plugin is loaded 12 print(f"Loading {self.name} v{self.version}") 13 14 # Register custom models, services, carriers, etc. 15 from . import carrier, models, services, hooks 16 17# Required: This allows Karrio to discover and load your plugin 18plugin = MyPlugin()

Adding a Custom Carrier

Create a carrier.py file to implement a custom shipping carrier:

plugins/my_plugin/carrier.py
1from karrio.core.models import ShippingRequest, ShippingResponse 2from karrio.core.carriers import Carrier 3 4class MyCustomCarrier(Carrier): 5 """Implementation for a custom shipping carrier.""" 6 7 carrier_name = 'my_custom_carrier' 8 display_name = 'My Custom Carrier' 9 10 def create_shipment(self, request: ShippingRequest) -> ShippingResponse: 11 # Implement shipment creation logic 12 # This is where you would integrate with the carrier's API 13 14 response = ShippingResponse( 15 carrier_name=self.carrier_name, 16 carrier_id='shipment_id_from_carrier', 17 tracking_number='tracking_number_from_carrier', 18 label_url='https://example.com/labels/example.pdf' 19 ) 20 21 return response 22 23 def track_shipment(self, tracking_number): 24 # Implement tracking logic 25 pass 26 27 def fetch_rates(self, request): 28 # Implement rate fetching logic 29 pass 30 31# Register the carrier with Karrio 32from karrio.core.registry import register_carrier 33register_carrier(MyCustomCarrier)

Implementing Event Hooks

Create a hooks.py file to implement custom event handlers:

plugins/my_plugin/hooks.py
1from karrio.core.events import on_event 2 3@on_event('shipment.created') 4def handle_shipment_created(shipment): 5 """Handle shipment created events.""" 6 print(f"Shipment created: {shipment.id}") 7 8 # You could integrate with your internal systems here 9 # For example, update inventory, notify stakeholders, etc. 10 11@on_event('shipment.tracked') 12def handle_shipment_tracked(tracking_update): 13 """Handle shipment tracking updates.""" 14 print(f"Tracking update for {tracking_update.tracking_number}: {tracking_update.status}")

Mounting the Plugins Directory

To make your plugins available to Karrio:

  1. Ensure your docker-compose.yml mounts the plugins directory:
1services: 2 api: 3 # ... other configuration 4 volumes: 5 - ./plugins:/karrio/plugins 6 7 worker: 8 # ... other configuration 9 volumes: 10 - ./plugins:/karrio/plugins
  1. Set the PLUGINS_DIR environment variable (though this is already set in our custom Dockerfile):
1services: 2 api: 3 environment: 4 # ... other environment variables 5 PLUGINS_DIR: /karrio/plugins

Installing Additional Python Packages

There are several approaches to installing additional Python packages:

Include the packages in your Dockerfile (as shown above):

1FROM karrio/server:latest 2RUN pip install your-package another-package

Method 2: Custom Requirements File

  1. Create a custom requirements file:
custom-requirements.txt
1your-package==1.0.0 2another-package==2.3.4
  1. Install it in your Dockerfile:
1COPY custom-requirements.txt /tmp/ 2RUN pip install -r /tmp/custom-requirements.txt

Method 3: Runtime Installation (Development Only)

For development, you can install packages at runtime:

  1. Add a startup script:
custom-startup.sh
1#!/bin/bash 2pip install your-package another-package 3./entrypoint
  1. Use it in your Dockerfile:
1COPY custom-startup.sh /karrio/app/ 2RUN chmod +x /karrio/app/custom-startup.sh 3CMD ["/karrio/app/custom-startup.sh"]

Advanced Customization Techniques

Custom API Endpoints

You can add custom API endpoints by creating Django views in your plugin:

plugins/my_plugin/views.py
1from django.http import JsonResponse 2from django.views.decorators.http import require_http_methods 3from django.contrib.auth.decorators import login_required 4 5@require_http_methods(["GET"]) 6def hello(request): 7 """A simple custom endpoint.""" 8 return JsonResponse({"message": "Hello from custom plugin!"}) 9 10@login_required 11@require_http_methods(["GET"]) 12def protected(request): 13 """A protected endpoint requiring authentication.""" 14 return JsonResponse({"message": f"Hello, {request.user.email}!"})

Then add the URLs to your plugin:

plugins/my_plugin/urls.py
1from django.urls import path 2from . import views 3 4urlpatterns = [ 5 path('hello/', views.hello, name='hello'), 6 path('protected/', views.protected, name='protected'), 7]

And register them in your plugin initialization:

In your __init__.py initialize method
1def initialize(self): 2 # Register URLs 3 from django.urls import include, path 4 from karrio.server.urls import urlpatterns 5 urlpatterns.append(path('api/custom/', include('my_plugin.urls')))

Database Extensions

You can add custom database models using Django’s ORM:

plugins/my_plugin/models.py
1from django.db import models 2from karrio.core.models import Shipment 3 4class CustomModel(models.Model): 5 """A custom database model.""" 6 name = models.CharField(max_length=100, db_index=True) 7 description = models.TextField(blank=True) 8 9 # You can relate to existing Karrio models 10 shipment = models.ForeignKey(Shipment, on_delete=models.CASCADE, related_name='custom_models') 11 12 def __str__(self): 13 return self.name

Then create and register migrations for your models:

In your __init__.py initialize method
1def initialize(self): 2 # Register models 3 from django.apps import apps 4 from django.conf import settings 5 6 if not apps.is_installed('my_plugin'): 7 settings.INSTALLED_APPS += ('my_plugin',) 8 9 # Run migrations 10 from django.core.management import call_command 11 call_command('makemigrations') 12 call_command('migrate')

Custom Dashboard Components

For frontend customizations, you can create custom React components for the dashboard.

  1. Create a dashboard directory in your project:
1mkdir -p dashboard/custom-components
  1. Create custom React components:
dashboard/custom-components/CustomWidget.jsx
1import React from 'react'; 2 3export const CustomWidget = () => { 4 return ( 5 <div className="custom-widget"> 6 <h3>Custom Widget</h3> 7 <p>This is a custom dashboard widget!</p> 8 </div> 9 ); 10};
  1. Create a custom dashboard Dockerfile:
Dockerfile.dashboard
1FROM karrio/dashboard:latest 2 3COPY dashboard/custom-components /app/custom-components 4 5# Install any frontend dependencies 6WORKDIR /app 7RUN npm install custom-package
  1. Update your docker-compose.yml:
1services: 2 dashboard: 3 build: 4 context: . 5 dockerfile: Dockerfile.dashboard 6 # ... rest of configuration

Deployment Considerations

Security

  • Never hardcode sensitive information in your Dockerfile or source code
  • Use environment variables for configuration
  • Consider using Docker secrets for sensitive information
  • Regularly update your dependencies

Performance

  • Be mindful of the resources your customizations require
  • Test your custom build under load
  • Monitor resource usage in production

Maintenance

  • Document your customizations thoroughly
  • Keep track of the changes you’ve made
  • Consider creating an automated build pipeline
  • Regularly rebase your custom builds on the latest Karrio releases

Examples

Example 1: Custom USPS Integration

plugins/custom_usps/carrier.py
1from karrio.core.carriers import Carrier 2from karrio.core.models import ShippingRequest, ShippingResponse 3 4class EnhancedUSPS(Carrier): 5 """Enhanced USPS integration with additional features.""" 6 7 carrier_name = 'enhanced_usps' 8 display_name = 'Enhanced USPS' 9 10 def create_shipment(self, request: ShippingRequest) -> ShippingResponse: 11 # Your enhanced USPS logic here 12 pass

Example 2: ERP Integration Plugin

plugins/erp_integration/__init__.py
1from karrio.core.plugins import Plugin 2 3class ERPIntegration(Plugin): 4 id = 'erp_integration' 5 name = 'ERP Integration' 6 version = '1.0.0' 7 8 def initialize(self): 9 from . import hooks, services 10 11plugin = ERPIntegration() 12 13# plugins/erp_integration/hooks.py 14from karrio.core.events import on_event 15import requests 16import os 17 18@on_event('shipment.created') 19def sync_to_erp(shipment): 20 """Sync new shipments to ERP system.""" 21 22 erp_url = os.environ.get('ERP_API_URL') 23 api_key = os.environ.get('ERP_API_KEY') 24 25 if not erp_url or not api_key: 26 print("ERP integration not configured") 27 return 28 29 # Transform shipment data for ERP 30 erp_data = { 31 'order_id': shipment.order_id, 32 'tracking_number': shipment.tracking_number, 33 'carrier': shipment.carrier_name, 34 'status': 'SHIPPED' 35 } 36 37 # Send to ERP 38 response = requests.post( 39 f"{erp_url}/orders/update", 40 json=erp_data, 41 headers={"Authorization": f"Bearer {api_key}"} 42 ) 43 44 if response.status_code != 200: 45 print(f"Failed to sync to ERP: {response.text}")

Troubleshooting

Plugin Not Loading

If your plugin isn’t loading:

  1. Check the logs:
1docker-compose logs api
  1. Verify the plugin directory is correctly mounted:
1docker-compose exec api ls -la /karrio/plugins
  1. Ensure your plugin has the correct structure and __init__.py file.

Import Errors

If you’re seeing import errors:

  1. Make sure your custom packages are properly installed
  2. Check that PYTHONPATH includes your plugins directory
  3. Verify package versions are compatible with Karrio

Database Errors

If you encounter database errors when adding custom models:

  1. Make sure your models are defined correctly
  2. Run migrations manually:
1docker-compose exec api karrio migrate

Conclusion

Custom Docker builds give you the flexibility to extend and customize Karrio to meet your specific needs. By following this guide, you can create a tailored Karrio deployment that integrates with your existing systems and adds custom functionality.

For more advanced customizations, consider contributing to the Karrio project directly or reaching out to the community for assistance.

Resources