Your start.sh script launches some background processes, then reaches the end, and exits (successfully, so with status code 0). When the start.sh script exits, since its the container's ENTRYPOINT, the container exits with the same status code.
There needs to be some process running as a foreground process, and the container will keep running as long as that process is. In your case that's the GUnicorn process, and you can specify that as the image's CMD in the Dockerfile:
CMD gunicorn --chdir hello --bind :8000 hello_django.wsgi:application
If the only thing in your start.sh script is running that line, you can delete the ENTRYPOINT line. If not, change it to run exec "$@" as the last line (to run the CMD), and change the Dockerfile ENTRYPOINT line to JSON-array syntax:
RUN chmod +x start.sh
ENTRYPOINT ["./start.sh"]
CMD gunicorn --chdir hello --bind :8000 hello_django.wsgi:application
#!/bin/sh
# ... do other pre-launch setup ...
# Run the CMD
exec "$@"
(I would avoid running background processes in a container startup script: nothing will monitor or restart those, and you can wind up in a state where you have a half-running container, or where you unnecessarily need to restart some things because others got updates. If you need multiple processes running, try to arrange them to run in separate containers; if you only need one, run it as the foreground process.)