Choose your deployment method and follow step-by-step instructions for successful installation
The fastest way to deploy ErrandLink is using Docker Compose.
tar -xzf errandlink-v1.0.0.tar.gz
cd errandlink
cp .env.example .env (do it for root, backend and frontend .env)
nano .env # Edit with your settings
sudo -u postgres psql
# In PostgreSQL shell:
CREATE DATABASE errandlink;
CREATE USER errandlink_user WITH ENCRYPTED PASSWORD 'your_secure_password';
GRANT ALL PRIVILEGES ON DATABASE errandlink TO errandlink_user;
ALTER DATABASE errandlink OWNER TO errandlink_user;
\q
docker compose up -d --build
Visit http://your-server-ip:5000/install in your browser to complete setup.
Laravel 11 API (PHP 8.2-FPM)
React + Vite (Nginx)
Node.js + Socket.IO
PostgreSQL 15
Redis 7
# View logs
docker compose logs -f
# Restart services
docker compose restart
# Stop all services
docker compose down
# Rebuild after changes
docker compose up -d --build
Recommended for production deployments.
sudo apt update && sudo apt upgrade -y
# Add PHP 8.2 repository
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
# Install all dependencies
sudo apt install -y \
nginx \
php8.2-fpm \
php8.2-pgsql \
php8.2-mysql \
php8.2-mbstring \
php8.2-xml \
php8.2-curl \
php8.2-zip \
php8.2-gd \
php8.2-redis \
php8.2-bcmath \
php8.2-intl \
postgresql \
postgresql-contrib \
redis-server \
composer \
nodejs \
npm \
unzip \
git
sudo -u postgres psql
# In PostgreSQL shell:
CREATE DATABASE errandlink;
CREATE USER errandlink_user WITH ENCRYPTED PASSWORD 'your_secure_password';
GRANT ALL PRIVILEGES ON DATABASE errandlink TO errandlink_user;
ALTER DATABASE errandlink OWNER TO errandlink_user;
\q
# Create web directory
sudo mkdir -p /var/www/errandlink
cd /var/www
# Upload errandlink-v1.0.0.tar.gz to server, then extract
sudo tar -xzf errandlink-v1.0.0.tar.gz -C errandlink --strip-components=1
# Set ownership
sudo chown -R www-data:www-data /var/www/errandlink
cd /var/www/errandlink/backend
# Install PHP dependencies
sudo -u www-data composer install --optimize-autoloader --no-dev
# Configure environment
sudo -u www-data cp .env.example .env
sudo nano .env # Edit with your database credentials
# Generate application key
sudo -u www-data php artisan key:generate
# Run database migrations
sudo -u www-data php artisan migrate --force
# Set permissions
sudo chmod -R 755 storage bootstrap/cache
sudo chmod 644 .env
Package comes with ready-built dist/ folder
Create /etc/nginx/sites-available/errandlink:
# Frontend Server
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
root /var/www/errandlink/frontend/dist;
index index.html;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
location / {
try_files $uri $uri/ /index.html;
}
# Proxy API requests to backend
location /api {
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;
}
# Proxy WebSocket for real-time
location /socket.io {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# Backend API Server
server {
listen 8000;
server_name localhost;
root /var/www/errandlink/backend/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
Enable the site:
sudo ln -s /etc/nginx/sites-available/errandlink /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
cd /var/www/errandlink/realtime
npm install --production
# Install PM2 globally
sudo npm install -g pm2
# Start realtime server
pm2 start server.js --name errandlink-realtime
# Save PM2 configuration
pm2 save
pm2 startup
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
sudo ufw allow 'Nginx Full'
sudo ufw allow OpenSSH
sudo ufw enable
pm2 start "php /var/www/errandlink/backend/artisan queue:work" --name errandlink-queue
pm2 save
sudo crontab -e
# Add this line:
* * * * * cd /var/www/errandlink/backend && php artisan schedule:run >> /dev/null 2>&1
Visit https://yourdomain.com/install to complete setup and create your admin account.
For shared hosting environments with cPanel.
| Component | Requirement | Status |
|---|---|---|
| PHP | 8.2 or higher | Check cPanel |
| Database | PostgreSQL/MySQL | Available in cPanel |
| SSH Access | Recommended | Optional but helpful |
| Node.js | For frontend build | Required locally |
dist production folder
errandlink-v1.0.0.tar.gzfrontend/dist/* to public_html/backend/ outside public_html/home/username/errandlink_backend/cd ~
tar -xzf errandlink-v1.0.0.tar.gz
mv errandlink/frontend/dist/* public_html/
mv errandlink/backend ~/errandlink_backend
In cPanel interface:
yourusername_errandlinkyourusername_errandlinkVia SSH access your server:
cd ~/errandlink_backend
cp .env.example .env
nano .env
Update these essential settings:
APP_NAME=ErrandLink
APP_ENV=production
APP_DEBUG=false
APP_URL=https://yourdomain.com
DB_CONNECTION=pgsql # or mysql
DB_HOST=localhost
DB_PORT=5432 # or 3306 for MySQL
DB_DATABASE=yourusername_errandlink
DB_USERNAME=yourusername_dbuser
DB_PASSWORD=your_database_password
FRONTEND_URL=https://yourdomain.com
php artisan key:generate
php artisan migrate --force
chmod -R 755 ~/errandlink_backend/storage
chmod -R 755 ~/errandlink_backend/bootstrap/cache
chmod 644 ~/errandlink_backend/.env
api.yourdomain.com/home/username/errandlink_backend/publicVITE_API_URLAdd to public_html/.htaccess:
<IfModule mod_rewrite.c>
RewriteEngine On
# Proxy API requests to backend
RewriteCond %{REQUEST_URI} ^/api/
RewriteRule ^api/(.*)$ /home/username/errandlink_backend/public/index.php/$1 [L,QSA]
</IfModule>
In cPanel, go to Cron Jobs and add:
* * * * * cd /home/username/errandlink_backend && php artisan schedule:run >> /dev/null 2>&1
https://yourdomain.com/install to complete the web installation wizard and create your admin account
For Apache servers (alternative to Nginx).
Create /etc/apache2/sites-available/errandlink.conf:
<VirtualHost *:80>
ServerName yourdomain.com
ServerAlias www.yourdomain.com
DocumentRoot /var/www/errandlink/frontend/dist
<Directory /var/www/errandlink/frontend/dist>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
# SPA fallback
FallbackResource /index.html
</Directory>
# Proxy API to backend
ProxyPreserveHost On
ProxyPass /api http://127.0.0.1:8000/api
ProxyPassReverse /api http://127.0.0.1:8000/api
# Proxy WebSocket
ProxyPass /socket.io ws://127.0.0.1:3001/socket.io
ProxyPassReverse /socket.io ws://127.0.0.1:3001/socket.io
ErrorLog ${APACHE_LOG_DIR}/errandlink-error.log
CustomLog ${APACHE_LOG_DIR}/errandlink-access.log combined
</VirtualHost>
<VirtualHost *:8000>
ServerName localhost
DocumentRoot /var/www/errandlink/backend/public
<Directory /var/www/errandlink/backend/public>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
<FilesMatch \.php$>
SetHandler "proxy:unix:/var/run/php/php8.2-fpm.sock|fcgi://localhost"
</FilesMatch>
</VirtualHost>
sudo a2enmod rewrite proxy proxy_http proxy_wstunnel
sudo a2ensite errandlink.conf
sudo a2dissite 000-default.conf
sudo systemctl reload apache2
Complete these steps after successful installation
Visit https://yourdomain.com/install and follow the wizard:
In Admin Dashboard > Settings > Payment:
https://yourdomain.com/api/v1/stripe/webhookhttps://yourdomain.com/api/v1/paystack/webhookhttps://yourdomain.com/auth/google/callbackCommon issues and solutions for ErrandLink installation
# Check Laravel logs
tail -f /var/www/errandlink/backend/storage/logs/laravel.log
# Fix permissions
sudo chmod -R 755 storage bootstrap/cache
sudo chown -R www-data:www-data storage bootstrap/cache
# Clear cache
php artisan cache:clear
php artisan config:clear
php artisan view:clear
# Test PostgreSQL connection
psql -h localhost -U errandlink_user errandlink
# Check PostgreSQL is running
sudo systemctl status postgresql
# Restart PostgreSQL
sudo systemctl restart postgresql
# Check Nginx configuration
sudo nginx -t
# Restart Nginx
sudo systemctl reload nginx
# Verify frontend build exists
ls -la /var/www/errandlink/frontend/dist/
# Check PM2 status
pm2 status
# View realtime logs
pm2 logs errandlink-realtime
# Restart realtime server
pm2 restart errandlink-realtime
1. Verify CORS_ALLOWED_ORIGINS in backend .env
2. Check that frontend URL matches exactly (including https://)
3. Clear browser cache and try again
1. Verify webhook URLs in Stripe/Paystack dashboard
2. Check that webhooks can reach your server (no firewall blocking)
3. Verify webhook secrets match in .env
4. Check Laravel logs for webhook errors
backend/storage/logs/