├── Dockerfile ├── README.md ├── header.png └── nginx.conf.sigil /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM minio/minio:latest 2 | 3 | # Add user dokku with an individual UID 4 | RUN adduser -u 32769 -m -U dokku 5 | USER dokku 6 | 7 | # Create data directory for the user, where we will keep the data 8 | RUN mkdir -p /home/dokku/data 9 | 10 | # Add custom nginx.conf template for Dokku to use 11 | WORKDIR /app 12 | ADD nginx.conf.sigil . 13 | 14 | CMD ["minio", "server", "/home/dokku/data", "--console-address", ":9001"] 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](header.png) 2 | 3 | [![Minio Version](https://img.shields.io/badge/Minio-latest-blue.svg)]() [![Dokku Version](https://img.shields.io/badge/Dokku-v0.11.2-blue.svg)]() 4 | 5 | # Run Minio on Dokku 6 | 7 | ## Perquisites 8 | 9 | ### What is Minio? 10 | 11 | Minio is an object storage server, and API compatible with Amazon S3 cloud 12 | storage service. Read more at the [minio.io](https://www.minio.io/) website. 13 | 14 | ### What is Dokku? 15 | 16 | [Dokku](http://dokku.viewdocs.io/dokku/) is the smallest PaaS implementation 17 | you've ever seen - _Docker powered mini-Heroku_. 18 | 19 | ### Requirements 20 | 21 | * A working [Dokku host](http://dokku.viewdocs.io/dokku/getting-started/installation/) 22 | 23 | # Setup 24 | 25 | We are going to use the domain `minio.example.com` and Dokku app `minio` for 26 | demonstration purposes. Make sure to replace it. 27 | 28 | ## Create the app 29 | 30 | Log onto your Dokku Host to create the Minio app: 31 | 32 | ```bash 33 | dokku apps:create minio 34 | ``` 35 | 36 | ## Configuration 37 | 38 | ### Setting environment variables 39 | 40 | Minio uses two access keys (`ACCESS_KEY` and `SECRET_KEY`) for authentication 41 | and object management. The following commands sets a random strings for each 42 | access key. 43 | 44 | ```bash 45 | dokku config:set --no-restart minio MINIO_ROOT_USER=$(echo `openssl rand -base64 45` | tr -d \=+ | cut -c 1-20) 46 | dokku config:set --no-restart minio MINIO_ROOT_PASSWORD=$(echo `openssl rand -base64 45` | tr -d \=+ | cut -c 1-32) 47 | ``` 48 | 49 | To login in the browser or via API, you will need to supply both the 50 | `ACCESS_KEY` and `SECRET_KEY`. You can retrieve these at any time while logged 51 | in on your host running dokku via `dokku config minio`. 52 | 53 | > **Note:** if you do not set these keys, Minio will generate them during 54 | > startup and output them to the log (check if via `dokku logs minio`). You 55 | > will still need to set them manually. 56 | 57 | You'll also need to set other two environment variables: 58 | 59 | - `NGINX_MAX_REQUEST_BODY`: used in the custom `nginx.conf` for this Dokku app 60 | to allow uploads up to 15MB to the HTTP server (if the file size is greater 61 | than 15MB, `s3cmd` will split in 15MB parts). 62 | - `MINIO_DOMAIN`: used to tell Minio the domain name being used by the server. 63 | 64 | ```bash 65 | dokku config:set --no-restart minio NGINX_MAX_REQUEST_BODY=15M 66 | dokku config:set --no-restart minio MINIO_DOMAIN=minio.example.com 67 | ``` 68 | 69 | > **Note**: if you're using [s4cmd](https://github.com/bloomreach/s4cmd/) 70 | > instead, be sure to pass the following parameters: 71 | > `--multipart-split-size=15728640 --max-singlepart-upload-size=15728640`. 72 | 73 | 74 | ## Persistent storage 75 | 76 | To persists uploaded data between restarts, we create a folder on the host 77 | machine, add write permissions to the user defined in `Dockerfile` and tell 78 | Dokku to mount it to the app container. 79 | 80 | ```bash 81 | sudo mkdir -p /var/lib/dokku/data/storage/minio 82 | sudo chown 32769:32769 /var/lib/dokku/data/storage/minio 83 | dokku storage:mount minio /var/lib/dokku/data/storage/minio:/home/dokku/data 84 | ``` 85 | 86 | ## Domain setup 87 | 88 | To get the routing working, we need to apply a few settings. First we set 89 | the domain. 90 | 91 | ```bash 92 | dokku domains:set minio minio.example.com 93 | ``` 94 | 95 | The parent Dockerfile, provided by the [Minio 96 | project](https://github.com/minio/minio), exposes port `9000` for web requests. 97 | Dokku will set up this port for outside communication, as explained in [its 98 | documentation](http://dokku.viewdocs.io/dokku/advanced-usage/proxy-management/#proxy-port-mapping). 99 | Because we want Minio to be available on the default port `80` (or `443` for 100 | SSL), we need to fiddle around with the proxy settings. 101 | 102 | First add the correct port mapping for this project as defined in the parent 103 | `Dockerfile`. 104 | 105 | ```bash 106 | dokku proxy:ports-add minio http:80:9000 107 | dokku proxy:ports-add minio https:443:9000 108 | dokku proxy:ports-add minio https:9001:9001 109 | ``` 110 | 111 | Next remove the proxy mapping added by Dokku. 112 | 113 | ```bash 114 | dokku proxy:ports-remove minio http:80:5000 115 | ``` 116 | 117 | ## Push Minio to Dokku 118 | 119 | ### Grabbing the repository 120 | 121 | First clone this repository onto your machine. 122 | 123 | #### Via SSH 124 | 125 | ```bash 126 | git clone git@github.com:slypix/minio-dokku.git 127 | ``` 128 | 129 | #### Via HTTPS 130 | 131 | ```bash 132 | git clone https://github.com/slypix/minio-dokku.git 133 | ``` 134 | 135 | ### Set up git remote 136 | 137 | Now you need to set up your Dokku server as a remote. 138 | 139 | ```bash 140 | git remote add dokku dokku@example.com:minio 141 | ``` 142 | 143 | ### Push Minio 144 | 145 | Now we can push Minio to Dokku (_before_ moving on to the [next 146 | part](#domain-and-ssl-certificate)). 147 | 148 | ```bash 149 | git push dokku master 150 | ``` 151 | 152 | ## SSL certificate 153 | 154 | Last but not least, we can go an grab the SSL certificate from [Let's 155 | Encrypt](https://letsencrypt.org/). 156 | You'll need [dokku-letsencrypt plugin](https://github.com/dokku/dokku-letsencrypt) installed. If it's not, install by running: 157 | 158 | ```bash 159 | dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git 160 | ``` 161 | 162 | Now get the SSL certificate: 163 | 164 | ```bash 165 | dokku config:set --no-restart minio DOKKU_LETSENCRYPT_EMAIL=you@example.com 166 | dokku letsencrypt:enable minio 167 | dokku proxy:ports-set minio https:443:9000 168 | ``` 169 | 170 | > **Note**: you must execute these steps *after* pushing the app to Dokku 171 | > host. 172 | 173 | ## Wrapping up 174 | 175 | Your Minio instance should now be available on 176 | [minio.example.com](https://minio.example.com). 177 | -------------------------------------------------------------------------------- /header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimischi/minio-dokku/ac10b79f5d1d9f8a58062906724567ea7b3b033c/header.png -------------------------------------------------------------------------------- /nginx.conf.sigil: -------------------------------------------------------------------------------- 1 | {{ range $port_map := .PROXY_PORT_MAP | split " " }} 2 | {{ $port_map_list := $port_map | split ":" }} 3 | {{ $scheme := index $port_map_list 0 }} 4 | {{ $listen_port := index $port_map_list 1 }} 5 | {{ $upstream_port := index $port_map_list 2 }} 6 | 7 | {{ if eq $scheme "http" }} 8 | server { 9 | listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }}; 10 | listen {{ if $.NGINX_BIND_ADDRESS_IP4 }}{{ $.NGINX_BIND_ADDRESS_IP4 }}:{{end}}{{ $listen_port }}; 11 | {{ if $.NOSSL_SERVER_NAME }}server_name {{ $.NOSSL_SERVER_NAME }}; {{ end }} 12 | access_log {{ $.NGINX_ACCESS_LOG_PATH }}; 13 | error_log {{ $.NGINX_ERROR_LOG_PATH }}; 14 | client_max_body_size {{ var "NGINX_MAX_REQUEST_BODY" }}; 15 | {{ if (and (eq $listen_port "80") ($.SSL_INUSE)) }} 16 | return 301 https://$host:{{ $.PROXY_SSL_PORT }}$request_uri; 17 | {{ else }} 18 | location / { 19 | 20 | gzip on; 21 | gzip_min_length 1100; 22 | gzip_buffers 4 32k; 23 | gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml font/truetype application/x-font-ttf font/opentype application/vnd.ms-fontobject image/svg+xml; 24 | gzip_vary on; 25 | gzip_comp_level 6; 26 | 27 | proxy_pass http://{{ $.APP }}-{{ $upstream_port }}; 28 | proxy_http_version 1.1; 29 | proxy_set_header Upgrade $http_upgrade; 30 | proxy_set_header Connection $http_connection; 31 | proxy_set_header Host $http_host; 32 | proxy_set_header X-Forwarded-Proto $scheme; 33 | proxy_set_header X-Forwarded-For $remote_addr; 34 | proxy_set_header X-Forwarded-Port $server_port; 35 | proxy_set_header X-Request-Start $msec; 36 | } 37 | include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf; 38 | 39 | error_page 400 401 402 403 405 406 407 408 409 410 411 412 413 414 415 416 417 418 420 422 423 424 426 428 429 431 444 449 450 451 /400-error.html; 40 | location /400-error.html { 41 | root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; 42 | internal; 43 | } 44 | 45 | error_page 404 /404-error.html; 46 | location /404-error.html { 47 | root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; 48 | internal; 49 | } 50 | 51 | error_page 500 501 502 503 504 505 506 507 508 509 510 511 /500-error.html; 52 | location /500-error.html { 53 | root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; 54 | internal; 55 | } 56 | {{ end }} 57 | } 58 | {{ else if eq $scheme "https"}} 59 | server { 60 | listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }} ssl {{ if eq $.HTTP2_SUPPORTED "true" }}http2{{ else if eq $.SPDY_SUPPORTED "true" }}spdy{{ end }}; 61 | listen {{ if $.NGINX_BIND_ADDRESS_IP4 }}{{ $.NGINX_BIND_ADDRESS_IP4 }}:{{end}}{{ $listen_port }} ssl {{ if eq $.HTTP2_SUPPORTED "true" }}http2{{ else if eq $.SPDY_SUPPORTED "true" }}spdy{{ end }}; 62 | {{ if $.SSL_SERVER_NAME }}server_name {{ $.SSL_SERVER_NAME }}; {{ end }} 63 | {{ if $.NOSSL_SERVER_NAME }}server_name {{ $.NOSSL_SERVER_NAME }}; {{ end }} 64 | access_log {{ $.NGINX_ACCESS_LOG_PATH }}; 65 | error_log {{ $.NGINX_ERROR_LOG_PATH }}; 66 | client_max_body_size {{ var "NGINX_MAX_REQUEST_BODY" }}; 67 | 68 | ssl_certificate {{ $.APP_SSL_PATH }}/server.crt; 69 | ssl_certificate_key {{ $.APP_SSL_PATH }}/server.key; 70 | ssl_protocols TLSv1.2 {{ if eq $.TLS13_SUPPORTED "true" }}TLSv1.3{{ end }}; 71 | ssl_prefer_server_ciphers off; 72 | 73 | keepalive_timeout 70; 74 | {{ if and (eq $.SPDY_SUPPORTED "true") (ne $.HTTP2_SUPPORTED "true") }}add_header Alternate-Protocol {{ $.PROXY_SSL_PORT }}:npn-spdy/2;{{ end }} 75 | 76 | location / { 77 | 78 | gzip on; 79 | gzip_min_length 1100; 80 | gzip_buffers 4 32k; 81 | gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml font/truetype application/x-font-ttf font/opentype application/vnd.ms-fontobject image/svg+xml; 82 | gzip_vary on; 83 | gzip_comp_level 6; 84 | 85 | proxy_pass http://{{ $.APP }}-{{ $upstream_port }}; 86 | {{ if eq $.HTTP2_PUSH_SUPPORTED "true" }}http2_push_preload on; {{ end }} 87 | proxy_http_version 1.1; 88 | proxy_set_header Upgrade $http_upgrade; 89 | proxy_set_header Connection $http_connection; 90 | proxy_set_header Host $http_host; 91 | proxy_set_header X-Forwarded-Proto $scheme; 92 | proxy_set_header X-Forwarded-For $remote_addr; 93 | proxy_set_header X-Forwarded-Port $server_port; 94 | proxy_set_header X-Request-Start $msec; 95 | } 96 | include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf; 97 | 98 | error_page 400 401 402 403 405 406 407 408 409 410 411 412 413 414 415 416 417 418 420 422 423 424 426 428 429 431 444 449 450 451 /400-error.html; 99 | location /400-error.html { 100 | root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; 101 | internal; 102 | } 103 | 104 | error_page 404 /404-error.html; 105 | location /404-error.html { 106 | root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; 107 | internal; 108 | } 109 | 110 | error_page 500 501 503 504 505 506 507 508 509 510 511 /500-error.html; 111 | location /500-error.html { 112 | root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; 113 | internal; 114 | } 115 | 116 | error_page 502 /502-error.html; 117 | location /502-error.html { 118 | root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; 119 | internal; 120 | } 121 | } 122 | {{ else if eq $scheme "grpc"}} 123 | {{ if eq $.GRPC_SUPPORTED "true"}}{{ if eq $.HTTP2_SUPPORTED "true"}} 124 | server { 125 | listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }} http2; 126 | listen {{ if $.NGINX_BIND_ADDRESS_IP4 }}{{ $.NGINX_BIND_ADDRESS_IP4 }}:{{end}}{{ $listen_port }} http2; 127 | {{ if $.NOSSL_SERVER_NAME }}server_name {{ $.NOSSL_SERVER_NAME }}; {{ end }} 128 | access_log {{ $.NGINX_ACCESS_LOG_PATH }}; 129 | error_log {{ $.NGINX_ERROR_LOG_PATH }}; 130 | client_max_body_size {{ var "NGINX_MAX_REQUEST_BODY" }}; 131 | location / { 132 | grpc_pass grpc://{{ $.APP }}-{{ $upstream_port }}; 133 | } 134 | include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf; 135 | } 136 | {{ end }}{{ end }} 137 | {{ else if eq $scheme "grpcs"}} 138 | {{ if eq $.GRPC_SUPPORTED "true"}}{{ if eq $.HTTP2_SUPPORTED "true"}} 139 | server { 140 | listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }} ssl http2; 141 | listen {{ if $.NGINX_BIND_ADDRESS_IP4 }}{{ $.NGINX_BIND_ADDRESS_IP4 }}:{{end}}{{ $listen_port }} ssl http2; 142 | {{ if $.NOSSL_SERVER_NAME }}server_name {{ $.NOSSL_SERVER_NAME }}; {{ end }} 143 | access_log {{ $.NGINX_ACCESS_LOG_PATH }}; 144 | error_log {{ $.NGINX_ERROR_LOG_PATH }}; 145 | client_max_body_size {{ var "NGINX_MAX_REQUEST_BODY" }}; 146 | 147 | ssl_certificate {{ $.APP_SSL_PATH }}/server.crt; 148 | ssl_certificate_key {{ $.APP_SSL_PATH }}/server.key; 149 | ssl_protocols TLSv1.2 {{ if eq $.TLS13_SUPPORTED "true" }}TLSv1.3{{ end }}; 150 | ssl_prefer_server_ciphers off; 151 | 152 | location / { 153 | grpc_pass grpc://{{ $.APP }}-{{ $upstream_port }}; 154 | } 155 | include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf; 156 | } 157 | {{ end }}{{ end }} 158 | {{ end }} 159 | {{ end }} 160 | 161 | {{ if $.DOKKU_APP_WEB_LISTENERS }} 162 | {{ range $upstream_port := $.PROXY_UPSTREAM_PORTS | split " " }} 163 | upstream {{ $.APP }}-{{ $upstream_port }} { 164 | {{ range $listeners := $.DOKKU_APP_WEB_LISTENERS | split " " }} 165 | {{ $listener_list := $listeners | split ":" }} 166 | {{ $listener_ip := index $listener_list 0 }} 167 | server {{ $listener_ip }}:{{ $upstream_port }};{{ end }} 168 | } 169 | {{ end }}{{ end }} 170 | --------------------------------------------------------------------------------