I am new to spin but I really like the concept. I was about to purchase the pro but wanted to get the free version running to test around before that. But exatly that is what giving me headaches right now.
I have an ubuntu server setup and trying to provision and deploy from my Win11 + WSL2 machine to this server.
I always end up with an unhealty container:
echo ‘ Deploying Docker stack with compose files: docker-compose.yml docker-compose.prod.yml on 192.168.10.4…’ Deploying Docker stack with compose files: docker-compose.yml docker-compose.prod.yml on 192.168.10.4…
docker -H ssh://[email protected]:22 stack deploy --compose-file docker-compose.yml --compose-file docker-compose.prod.yml --detach=false --prune spin-production
Updating service spin-production_php (id: mt51k80o08rplzgryz8tjuu1g)
Updating service spin-production_traefik (id: j8h7jk0gb2msc2cx7jar9y8uz)
overall progress: 0 out of 1 tasks
1/1: task: non-zero exit (137): dockerexec: unhealthy container
can anyone point me in the direction how to debug what causes the unhealty container??
services:
traefik:
ports:
- target: 80
published: 80
protocol: tcp
mode: host
- target: 443
published: 443
protocol: tcp
mode: host
networks:
- web-public
deploy:
update_config:
parallelism: 1
delay: 5s
order: stop-first
placement:
constraints:
- node.role==manager
volumes:
# Mount the Docker socket as read-only so Traefik can listen to events
- /var/run/docker.sock:/var/run/docker.sock:ro
- certificates:/certificates
configs:
- source: traefik
target: /etc/traefik/traefik.yml
php:
image: ${SPIN_IMAGE_DOCKERFILE} # 👈 Change this if you're not using `spin deploy`
environment:
#PHP_OPCACHE_ENABLE: "1"
#AUTORUN_ENABLED: "true" # 👈 Remove this line if you don't want Laravel Automations
APP_ENV: "${SPIN_DEPLOYMENT_ENVIRONMENT}" # 👈 Remove this if you're not using `spin deploy`
HEALTHCHECK_PATH: "/up" # Use Laravel's built-in health route
SSL_MODE: full
LOG_OUTPUT_LEVEL: debug
networks:
- web-public
volumes:
- "storage_private:/var/www/html/storage/app/private/"
- "storage_public:/var/www/html/storage/app/public/"
- "storage_sessions:/var/www/html/storage/framework/sessions"
- "storage_logs:/var/www/html/storage/logs"
- "database_sqlite:/var/www/html/.infrastructure/volume_data/sqlite"
deploy:
replicas: 1
update_config:
failure_action: rollback
parallelism: 1
delay: 5s
order: start-first
rollback_config:
parallelism: 0
order: stop-first
restart_policy:
condition: any
delay: 10s
max_attempts: 3
window: 120s
labels:
- "traefik.enable=true"
- "traefik.http.routers.my-php-app.rule=Host(`${SPIN_APP_DOMAIN}`)"
- "traefik.http.routers.my-php-app.entrypoints=websecure"
- "traefik.http.routers.my-php-app.tls=true"
- "traefik.http.routers.my-php-app.tls.certresolver=letsencryptresolver"
- "traefik.http.services.my-php-app.loadbalancer.server.port=8443"
- "traefik.http.services.my-php-app.loadbalancer.server.scheme=https"
# Health check
- "traefik.http.services.my-php-app.loadbalancer.healthcheck.path=/up"
- "traefik.http.services.my-php-app.loadbalancer.healthcheck.interval=30s"
- "traefik.http.services.my-php-app.loadbalancer.healthcheck.timeout=5s"
- "traefik.http.services.my-php-app.loadbalancer.healthcheck.scheme=http"
configs:
traefik:
name: "traefik-${SPIN_MD5_HASH_TRAEFIK_YML}.yml"
file: ./.infrastructure/conf/traefik/prod/traefik.yml
volumes:
certificates:
storage_private:
storage_public:
storage_sessions:
storage_logs:
database_sqlite:
networks:
web-public:
.spin.yml
##############################################################
# ???? Users - You must set at least one user
##############################################################
users:
- username: alex
name: Alex
groups: ['sudo']
password: "password"
authorized_keys:
- public_key: "pubkey"
- username: deploy
name: Deploy
groups: ['sudo']
password: "password"
authorized_keys:
- public_key: "pubkey"
##############################################################
# ???? Providers - You must set at least one provider
##############################################################
providers:
- name: selfhost
# - name: digitalocean
# api_token: Set token here OR delete this line and set environment variable DO_API_TOKEN
# - name: hetzner
# api_token: Set token here OR delete this line and set environment variable HCLOUD_TOKEN
# - name: vultr
# api_token: Set token here OR delete this line and set environment variable VULTR_API_KEY
##############################################################
# ???? Servers - You must set at least one server
##############################################################
servers:
- server_name: ubuntu-home
environment: production
address: 192.168.10.4
# hardware_profile: ubuntu-home-profile
# - server_name: ubuntu-1gb-ord-2
# environment: staging
# hardware_profile: vultr_1c_1gb_ubuntu2404
##############################################################
# ???? Hardware Profiles
##############################################################
hardware_profiles:
# Hetzner
- name: hetzner_2c_2gb_ubuntu2404
provider: hetzner
profile_config:
location: ash
server_type: cpx11
image: ubuntu-24.04
backups: true
# Vultr
- name: vultr_1c_1gb_ubuntu2404
provider: vultr
profile_config:
region: ord
plan: vc2-1c-1gb
os: "Ubuntu 24.04 LTS x64"
backups: true
# DigitalOcean
- name: digitalocean_1c_1gb_ubuntu2404
provider: digitalocean
profile_config:
region: nyc3
size: s-1vcpu-1gb
image: ubuntu-24-04-x64
backups: true
##############################################################
# ???? Environments
##############################################################
environments:
- name: production
- name: staging
- name: development
##############################################################
# ???? Advanced Server Configuration
##############################################################
# Timezone and contact settings
server_timezone: "Etc/UTC"
server_contact: [email protected]
# If you the SSH port below, you may need to run `spin provision -p <your-default-ssh-port>`
# to get a connection on your first provision. Otherwise, SSH will try connecting
# to your new port before the SSH server configuration is updated.
ssh_port: "22"
## You can set this to false to require a password for sudo.
## If you disable passwordless sudo, you must set a password for all sudo users.
## generate an encrypted hash with `spin mkpasswd`. Learn more:
## https://serversideup.net/open-source/spin/docs/command-reference/mkpasswd
use_passwordless_sudo: true
## Email Notifications
postfix_hostname: "{{ inventory_hostname }}"
## Set variables below to enable external SMTP relay
# postfix_relayhost: "smtp.example.com"
# postfix_relayhost_port: "587"
# postfix_relayhost_username: "myusername"
# postfix_relayhost_password: "mysupersecretpassword"
## Deploy user customization - You can customize the deploy user below if you'd like
# docker_user:
# username: deploy
# home: /opt/deploy
# authorized_ssh_keys:
# - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKNJGtd7a4DBHsQi7HGrC5xz0eAEFHZ3Ogh3FEFI2345 fake@key"
# - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFRfXxUZ8q9vHRcQZ6tLb0KwGHu8xjQHfYopZKLmnopQ anotherfake@key"
Dockerfile
############################################
# Base Image
############################################
# Learn more about the Server Side Up PHP Docker Images at:
# https://serversideup.net/open-source/docker-php/
FROM serversideup/php:8.4-fpm-nginx-alpine AS base
## Uncomment if you need to install additional PHP extensions
USER root
RUN install-php-extensions intl exif
############################################
# Development Image
############################################
FROM base AS development
# We can pass USER_ID and GROUP_ID as build arguments
# to ensure the www-data user has the same UID and GID
# as the user running Docker.
ARG USER_ID
ARG GROUP_ID
# Switch to root so we can set the user ID and group ID
USER root
# Set the user ID and group ID for www-data
RUN docker-php-serversideup-set-id www-data $USER_ID:$GROUP_ID && \
docker-php-serversideup-set-file-permissions --owner $USER_ID:$GROUP_ID --service nginx
# Drop privileges back to www-data
USER www-data
############################################
# CI image
############################################
FROM base AS ci
# Sometimes CI images need to run as root
# so we set the ROOT user and configure
# the PHP-FPM pool to run as www-data
USER root
RUN echo "user = www-data" >> /usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf && \
echo "group = www-data" >> /usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf
############################################
# Production Image
############################################
FROM base AS deploy
COPY --chown=www-data:www-data . /var/www/html
# Create the SQLite directory and set the owner to www-data (remove this if you're not using SQLite)
RUN mkdir -p /var/www/html/.infrastructure/volume_data/sqlite/ && \
chown -R www-data:www-data /var/www/html/.infrastructure/volume_data/sqlite/
USER www-data
This can be closed. It works without the healthchecks. What I found out is that even though I am using Laravel11, the template I started with does not have the /up route … so that was a problem for sure.
I have to play around a bit more with getting the health checks to work. But I think I can handle troubleshooting from now on, now that you have pointed me in the right direction.