The first time you start Geth, it starts in a "fast sync" mode where it quickly downloads a pruned copy of the blockchain instead of all blocks. Geth isn't able to resume a fast sync: if you restart Geth before it finishes, then it switches to its regular mode after that where it has to download every full block, which is overwhelmingly slow and takes a lot of disk space.
You could try using the Parity client for Ethereum instead. It automatically uses a fast sync mode, it handles resuming the fast sync, and it continuously prunes the blockchain as new blocks come in. Parity even works in place of Geth locally with applications like Mist. I had the same problem as you with Geth, and switching to Parity solved my problem of never managing to sync the blockchain.